diff --git a/skills/instrumentation/discover-event-surfaces/references/best-practices.md b/skills/instrumentation/discover-event-surfaces/references/best-practices.md index 0e925c41d..e3fdfe154 100644 --- a/skills/instrumentation/discover-event-surfaces/references/best-practices.md +++ b/skills/instrumentation/discover-event-surfaces/references/best-practices.md @@ -324,7 +324,7 @@ These standards apply when helping customers design new events, add properties, - **Past tense is the Amplitude standard** — events, by definition, record things that have already happened. However, if the project is consistently using another tense, prefer consistency over the ideal. - Event names can be descriptive without over-specifying. A vague name like `Page Viewed` is preferable to creating a separate event for every page — use a `page_path` property to break it down. Similarly, `Error Encountered` paired with an `error_type` property is better than separate events per error. Avoid names so specific they create taxonomy noise (`Home Page Hero Banner Clicked on iPad`). - Use identical names across all platforms (iOS, Android, Web, server-side). Platform differences belong in a `platform` property, not the event name. -- **Autocapture covers several common events** — do not recommend instrumenting precision tracked events for anything already captured by Autocapture: `Page Viewed`, `Element Clicked`, `Element Changed`, `Form Started`, and others. Autocapture events include additional configuration options, out-of-the-box charts, and a rich set of tested properties that precision tracked events lack. +- **Autocapture covers a fixed set of events** — do not recommend precision-tracked equivalents for events already emitted by Amplitude Browser SDK Autocapture. The full excluded list: `[Amplitude] Page Viewed`, `[Amplitude] Start Session`, `[Amplitude] End Session`, `[Amplitude] Form Started`, `[Amplitude] Form Submitted`, `[Amplitude] File Downloaded`, `[Amplitude] Element Clicked`, `[Amplitude] Element Changed`, `[Amplitude] Rage Click`, `[Amplitude] Dead Click`, `[Amplitude] Error Click`, `[Amplitude] Thrashed Cursor`, `[Amplitude] Network Request`, and `[Amplitude] Web Vitals`. Also avoid conceptual duplicates (e.g., `Button Clicked`, `Session Started`, `Link Clicked`) — they duplicate Autocapture even without the `[Amplitude]` prefix. Autocapture events include additional configuration options, out-of-the-box charts, and a rich set of tested properties that precision-tracked events lack. Canonical source: `skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md` — keep both in sync. **Property naming:** - Use identical property names across all events when they capture the same concept. `product_name` must be `product_name` everywhere — not `name`, `product_name`, or `prod_name` on different events. diff --git a/skills/instrumentation/instrument-events/references/best-practices.md b/skills/instrumentation/instrument-events/references/best-practices.md index 0e925c41d..e3fdfe154 100644 --- a/skills/instrumentation/instrument-events/references/best-practices.md +++ b/skills/instrumentation/instrument-events/references/best-practices.md @@ -324,7 +324,7 @@ These standards apply when helping customers design new events, add properties, - **Past tense is the Amplitude standard** — events, by definition, record things that have already happened. However, if the project is consistently using another tense, prefer consistency over the ideal. - Event names can be descriptive without over-specifying. A vague name like `Page Viewed` is preferable to creating a separate event for every page — use a `page_path` property to break it down. Similarly, `Error Encountered` paired with an `error_type` property is better than separate events per error. Avoid names so specific they create taxonomy noise (`Home Page Hero Banner Clicked on iPad`). - Use identical names across all platforms (iOS, Android, Web, server-side). Platform differences belong in a `platform` property, not the event name. -- **Autocapture covers several common events** — do not recommend instrumenting precision tracked events for anything already captured by Autocapture: `Page Viewed`, `Element Clicked`, `Element Changed`, `Form Started`, and others. Autocapture events include additional configuration options, out-of-the-box charts, and a rich set of tested properties that precision tracked events lack. +- **Autocapture covers a fixed set of events** — do not recommend precision-tracked equivalents for events already emitted by Amplitude Browser SDK Autocapture. The full excluded list: `[Amplitude] Page Viewed`, `[Amplitude] Start Session`, `[Amplitude] End Session`, `[Amplitude] Form Started`, `[Amplitude] Form Submitted`, `[Amplitude] File Downloaded`, `[Amplitude] Element Clicked`, `[Amplitude] Element Changed`, `[Amplitude] Rage Click`, `[Amplitude] Dead Click`, `[Amplitude] Error Click`, `[Amplitude] Thrashed Cursor`, `[Amplitude] Network Request`, and `[Amplitude] Web Vitals`. Also avoid conceptual duplicates (e.g., `Button Clicked`, `Session Started`, `Link Clicked`) — they duplicate Autocapture even without the `[Amplitude]` prefix. Autocapture events include additional configuration options, out-of-the-box charts, and a rich set of tested properties that precision-tracked events lack. Canonical source: `skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md` — keep both in sync. **Property naming:** - Use identical property names across all events when they capture the same concept. `product_name` must be `product_name` everywhere — not `name`, `product_name`, or `prod_name` on different events. diff --git a/skills/integration/integration-android/SKILL.md b/skills/integration/integration-android/SKILL.md index 188e173c9..b66cd7423 100644 --- a/skills/integration/integration-android/SKILL.md +++ b/skills/integration/integration-android/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Android applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Android example project code -- `references/android.md` - Amplitude documentation for Android +- `references/android.md` - Android - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-android/references/amplitude-quickstart.md b/skills/integration/integration-android/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-android/references/amplitude-quickstart.md +++ b/skills/integration/integration-android/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

-

Visit Amplitude.com

-

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-android/references/android.md b/skills/integration/integration-android/references/android.md index 11d2f21b5..41db9528c 100644 --- a/skills/integration/integration-android/references/android.md +++ b/skills/integration/integration-android/references/android.md @@ -1,1468 +1,51 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Android | Amplitude - - - - - - - - - - - - - - - - - - +[Amplitude AI](/docs/amplitude-ai) - - - +[Admin](/docs/admin) +[Partners](/docs/partners) - - -
-
-
-
- +[FAQ](/docs/faq) - +[SDKs](/docs/sdks) -
-
+/ -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) +/ -
-
+[Android](/docs/sdks/analytics/android) -
- -
- -
- - - - - - - - +# Android - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +`current` [## ![](/docs/assets/icons/java.svg) ![](/docs/assets/icons/kotlin.svg) Android-Kotlin SDK](/docs/sdks/analytics/android/android-kotlin-sdk) -
- -
-
-
-
- -
-
-
-

Android

- -
-
- -
- current - -

- - - - - - Android-Kotlin SDK -

-
-
-
- -
- - - -
-
-
-
-
- -
- - +- [GitHub](https://github.com/amplitude/Amplitude-Kotlin) +- [Releases](https://github.com/amplitude/Amplitude-Kotlin/releases) +- [Ampli](/docs/sdks/analytics/android/ampli-for-android-kotlin-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Android) +- [Releases](https://github.com/amplitude/Amplitude-Android/releases) +- [Migrate to the Android-Kotlin SDK](/docs/sdks/analytics/android/migrate-to-the-android-kotlin-sdk) \ No newline at end of file diff --git a/skills/integration/integration-angular/SKILL.md b/skills/integration/integration-angular/SKILL.md index 03d956f6c..29be37f2e 100644 --- a/skills/integration/integration-angular/SKILL.md +++ b/skills/integration/integration-angular/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Angular applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Angular example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-angular/references/amplitude-quickstart.md b/skills/integration/integration-angular/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-angular/references/amplitude-quickstart.md +++ b/skills/integration/integration-angular/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

-

Visit Amplitude.com

-

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-angular/references/browser-sdk-2.md b/skills/integration/integration-angular/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-angular/references/browser-sdk-2.md +++ b/skills/integration/integration-angular/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

-

Visit Amplitude.com

-

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-angular/references/browser-unified-sdk.md b/skills/integration/integration-angular/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-angular/references/browser-unified-sdk.md +++ b/skills/integration/integration-angular/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-astro-hybrid/SKILL.md b/skills/integration/integration-astro-hybrid/SKILL.md index f13fd2b6d..6c53fd336 100644 --- a/skills/integration/integration-astro-hybrid/SKILL.md +++ b/skills/integration/integration-astro-hybrid/SKILL.md @@ -16,16 +16,16 @@ This skill helps you add Amplitude analytics to Astro (Hybrid) applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Astro (Hybrid) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-astro-hybrid/references/EXAMPLE.md b/skills/integration/integration-astro-hybrid/references/EXAMPLE.md index 362404b01..d4dbae946 100644 --- a/skills/integration/integration-astro-hybrid/references/EXAMPLE.md +++ b/skills/integration/integration-astro-hybrid/references/EXAMPLE.md @@ -98,238 +98,6 @@ pnpm preview --- -## .astro/content-assets.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content-modules.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content.d.ts - -```ts -declare module 'astro:content' { - export interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - export type ReferenceDataEntry< - C extends CollectionKey, - E extends keyof DataEntryMap[C] = string, - > = { - collection: C; - id: E; - }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; - export type ReferenceLiveEntry = { - collection: C; - id: string; - }; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getLiveCollection( - collection: C, - filter?: LiveLoaderCollectionFilterType, - ): Promise< - import('astro').LiveDataCollectionResult, LiveLoaderErrorType> - >; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - entry: ReferenceDataEntry, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? string extends keyof DataEntryMap[C] - ? Promise | undefined - : Promise - : Promise | undefined>; - export function getLiveEntry( - collection: C, - filter: string | LiveLoaderEntryFilterType, - ): Promise, LiveLoaderErrorType>>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; - export function getEntries( - entries: ReferenceDataEntry[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - - }; - - type DataEntryMap = { - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< - infer TData, - infer TEntryFilter, - infer TCollectionFilter, - infer TError - > - ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } - : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; - type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; - type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; - type ExtractErrorType = ExtractLoaderTypes['error']; - - type LiveLoaderDataType = - LiveContentConfig['collections'][C]['schema'] extends undefined - ? ExtractDataType - : import('astro/zod').infer< - Exclude - >; - type LiveLoaderEntryFilterType = - ExtractEntryFilterType; - type LiveLoaderCollectionFilterType = - ExtractCollectionFilterType; - type LiveLoaderErrorType = ExtractErrorType< - LiveContentConfig['collections'][C]['loader'] - >; - - export type ContentConfig = typeof import("../src/content.config.mjs"); - export type LiveContentConfig = never; -} - -``` - ---- - -## .astro/types.d.ts - -```ts -/// -/// -``` - ---- - ## .env.example ```example diff --git a/skills/integration/integration-astro-hybrid/references/amplitude-quickstart.md b/skills/integration/integration-astro-hybrid/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-astro-hybrid/references/amplitude-quickstart.md +++ b/skills/integration/integration-astro-hybrid/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-astro-hybrid/references/browser-sdk-2.md b/skills/integration/integration-astro-hybrid/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-astro-hybrid/references/browser-sdk-2.md +++ b/skills/integration/integration-astro-hybrid/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-astro-hybrid/references/browser-unified-sdk.md b/skills/integration/integration-astro-hybrid/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-astro-hybrid/references/browser-unified-sdk.md +++ b/skills/integration/integration-astro-hybrid/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-astro-ssr/SKILL.md b/skills/integration/integration-astro-ssr/SKILL.md index ea906ca8b..2c1aea53f 100644 --- a/skills/integration/integration-astro-ssr/SKILL.md +++ b/skills/integration/integration-astro-ssr/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Astro (SSR) applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Astro (SSR) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-astro-ssr/references/EXAMPLE.md b/skills/integration/integration-astro-ssr/references/EXAMPLE.md index 584348e3e..758e52239 100644 --- a/skills/integration/integration-astro-ssr/references/EXAMPLE.md +++ b/skills/integration/integration-astro-ssr/references/EXAMPLE.md @@ -120,238 +120,6 @@ pnpm preview --- -## .astro/content-assets.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content-modules.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content.d.ts - -```ts -declare module 'astro:content' { - export interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - export type ReferenceDataEntry< - C extends CollectionKey, - E extends keyof DataEntryMap[C] = string, - > = { - collection: C; - id: E; - }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; - export type ReferenceLiveEntry = { - collection: C; - id: string; - }; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getLiveCollection( - collection: C, - filter?: LiveLoaderCollectionFilterType, - ): Promise< - import('astro').LiveDataCollectionResult, LiveLoaderErrorType> - >; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - entry: ReferenceDataEntry, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? string extends keyof DataEntryMap[C] - ? Promise | undefined - : Promise - : Promise | undefined>; - export function getLiveEntry( - collection: C, - filter: string | LiveLoaderEntryFilterType, - ): Promise, LiveLoaderErrorType>>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; - export function getEntries( - entries: ReferenceDataEntry[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - - }; - - type DataEntryMap = { - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< - infer TData, - infer TEntryFilter, - infer TCollectionFilter, - infer TError - > - ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } - : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; - type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; - type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; - type ExtractErrorType = ExtractLoaderTypes['error']; - - type LiveLoaderDataType = - LiveContentConfig['collections'][C]['schema'] extends undefined - ? ExtractDataType - : import('astro/zod').infer< - Exclude - >; - type LiveLoaderEntryFilterType = - ExtractEntryFilterType; - type LiveLoaderCollectionFilterType = - ExtractCollectionFilterType; - type LiveLoaderErrorType = ExtractErrorType< - LiveContentConfig['collections'][C]['loader'] - >; - - export type ContentConfig = typeof import("../src/content.config.mjs"); - export type LiveContentConfig = never; -} - -``` - ---- - -## .astro/types.d.ts - -```ts -/// -/// -``` - ---- - ## .env.example ```example diff --git a/skills/integration/integration-astro-ssr/references/amplitude-quickstart.md b/skills/integration/integration-astro-ssr/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-astro-ssr/references/amplitude-quickstart.md +++ b/skills/integration/integration-astro-ssr/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-astro-ssr/references/browser-sdk-2.md b/skills/integration/integration-astro-ssr/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-astro-ssr/references/browser-sdk-2.md +++ b/skills/integration/integration-astro-ssr/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-astro-ssr/references/browser-unified-sdk.md b/skills/integration/integration-astro-ssr/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-astro-ssr/references/browser-unified-sdk.md +++ b/skills/integration/integration-astro-ssr/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-astro-static/SKILL.md b/skills/integration/integration-astro-static/SKILL.md index 9e117a924..17896bb8e 100644 --- a/skills/integration/integration-astro-static/SKILL.md +++ b/skills/integration/integration-astro-static/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Astro (Static) applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Astro (Static) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-astro-static/references/EXAMPLE.md b/skills/integration/integration-astro-static/references/EXAMPLE.md index 4c0f9fee6..29aeca55d 100644 --- a/skills/integration/integration-astro-static/references/EXAMPLE.md +++ b/skills/integration/integration-astro-static/references/EXAMPLE.md @@ -15,7 +15,7 @@ This is an [Astro](https://astro.build/) static site (SSG) example demonstrating This static site uses only the [Browser Unified SDK (npm)](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#unified-sdk-npm). [`@amplitude/unified`](https://www.npmjs.com/package/@amplitude/unified) is bundled in `src/components/amplitude.astro`; `initAll` runs on the client. [Initialize the Unified SDK](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#initialize-the-unified-sdk) documents that call as initializing every product bundled with Unified npm. See [Unified SDK configuration](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#configuration) for `analytics`, `sessionReplay`, `experiment`, and `engagement`. The `experiment` block is **Feature Experiment** (`@amplitude/experiment-js-client`). Amplitude’s [product support table](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#product-support-by-installation-method) lists **Web Experiment** (`@amplitude/experiment-tag`, including the visual editor) for the [CDN script](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#unified-script-cdn), not Unified **npm**. -There is no server SDK in this sample. For Node or API routes, use [`@amplitude/analytics-node`](https://www.npmjs.com/package/@amplitude/analytics-node) (see [astro-ssr](../astro-ssr)). +There is no server SDK in this sample. For Node or API routes, use [`@amplitude/analytics-node`](https://www.npmjs.com/package/@amplitude/analytics-node) (see [astro-ssr](../../integration-astro-ssr/)). The layout assigns the unified namespace to `window.amplitude` so `is:inline` scripts can call `track`, `setUserId`, and `reset` without importing the package on every page (see [accessing SDK features](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#access-sdk-features) in the official doc). @@ -131,238 +131,6 @@ pnpm preview --- -## .astro/content-assets.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content-modules.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content.d.ts - -```ts -declare module 'astro:content' { - export interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - export type ReferenceDataEntry< - C extends CollectionKey, - E extends keyof DataEntryMap[C] = string, - > = { - collection: C; - id: E; - }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; - export type ReferenceLiveEntry = { - collection: C; - id: string; - }; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getLiveCollection( - collection: C, - filter?: LiveLoaderCollectionFilterType, - ): Promise< - import('astro').LiveDataCollectionResult, LiveLoaderErrorType> - >; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - entry: ReferenceDataEntry, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? string extends keyof DataEntryMap[C] - ? Promise | undefined - : Promise - : Promise | undefined>; - export function getLiveEntry( - collection: C, - filter: string | LiveLoaderEntryFilterType, - ): Promise, LiveLoaderErrorType>>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; - export function getEntries( - entries: ReferenceDataEntry[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - - }; - - type DataEntryMap = { - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< - infer TData, - infer TEntryFilter, - infer TCollectionFilter, - infer TError - > - ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } - : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; - type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; - type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; - type ExtractErrorType = ExtractLoaderTypes['error']; - - type LiveLoaderDataType = - LiveContentConfig['collections'][C]['schema'] extends undefined - ? ExtractDataType - : import('astro/zod').infer< - Exclude - >; - type LiveLoaderEntryFilterType = - ExtractEntryFilterType; - type LiveLoaderCollectionFilterType = - ExtractCollectionFilterType; - type LiveLoaderErrorType = ExtractErrorType< - LiveContentConfig['collections'][C]['loader'] - >; - - export type ContentConfig = typeof import("../src/content.config.mjs"); - export type LiveContentConfig = never; -} - -``` - ---- - -## .astro/types.d.ts - -```ts -/// -/// -``` - ---- - ## .env.example ```example diff --git a/skills/integration/integration-astro-static/references/amplitude-quickstart.md b/skills/integration/integration-astro-static/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-astro-static/references/amplitude-quickstart.md +++ b/skills/integration/integration-astro-static/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-astro-static/references/browser-sdk-2.md b/skills/integration/integration-astro-static/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-astro-static/references/browser-sdk-2.md +++ b/skills/integration/integration-astro-static/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-astro-static/references/browser-unified-sdk.md b/skills/integration/integration-astro-static/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-astro-static/references/browser-unified-sdk.md +++ b/skills/integration/integration-astro-static/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-astro-view-transitions/SKILL.md b/skills/integration/integration-astro-view-transitions/SKILL.md index 0b190913c..0a2a3ffc1 100644 --- a/skills/integration/integration-astro-view-transitions/SKILL.md +++ b/skills/integration/integration-astro-view-transitions/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Astro (View Transitions) applica Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Astro (View Transitions) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-astro-view-transitions/references/EXAMPLE.md b/skills/integration/integration-astro-view-transitions/references/EXAMPLE.md index 6006341ed..a55a97770 100644 --- a/skills/integration/integration-astro-view-transitions/references/EXAMPLE.md +++ b/skills/integration/integration-astro-view-transitions/references/EXAMPLE.md @@ -144,238 +144,6 @@ pnpm preview --- -## .astro/content-assets.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content-modules.mjs - -```mjs -export default new Map(); -``` - ---- - -## .astro/content.d.ts - -```ts -declare module 'astro:content' { - export interface RenderResult { - Content: import('astro/runtime/server/index.js').AstroComponentFactory; - headings: import('astro').MarkdownHeading[]; - remarkPluginFrontmatter: Record; - } - interface Render { - '.md': Promise; - } - - export interface RenderedContent { - html: string; - metadata?: { - imagePaths: Array; - [key: string]: unknown; - }; - } -} - -declare module 'astro:content' { - type Flatten = T extends { [K: string]: infer U } ? U : never; - - export type CollectionKey = keyof AnyEntryMap; - export type CollectionEntry = Flatten; - - export type ContentCollectionKey = keyof ContentEntryMap; - export type DataCollectionKey = keyof DataEntryMap; - - type AllValuesOf = T extends any ? T[keyof T] : never; - type ValidContentEntrySlug = AllValuesOf< - ContentEntryMap[C] - >['slug']; - - export type ReferenceDataEntry< - C extends CollectionKey, - E extends keyof DataEntryMap[C] = string, - > = { - collection: C; - id: E; - }; - export type ReferenceContentEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}) = string, - > = { - collection: C; - slug: E; - }; - export type ReferenceLiveEntry = { - collection: C; - id: string; - }; - - /** @deprecated Use `getEntry` instead. */ - export function getEntryBySlug< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - // Note that this has to accept a regular string too, for SSR - entrySlug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - - /** @deprecated Use `getEntry` instead. */ - export function getDataEntryById( - collection: C, - entryId: E, - ): Promise>; - - export function getCollection>( - collection: C, - filter?: (entry: CollectionEntry) => entry is E, - ): Promise; - export function getCollection( - collection: C, - filter?: (entry: CollectionEntry) => unknown, - ): Promise[]>; - - export function getLiveCollection( - collection: C, - filter?: LiveLoaderCollectionFilterType, - ): Promise< - import('astro').LiveDataCollectionResult, LiveLoaderErrorType> - >; - - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - entry: ReferenceContentEntry, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - entry: ReferenceDataEntry, - ): E extends keyof DataEntryMap[C] - ? Promise - : Promise | undefined>; - export function getEntry< - C extends keyof ContentEntryMap, - E extends ValidContentEntrySlug | (string & {}), - >( - collection: C, - slug: E, - ): E extends ValidContentEntrySlug - ? Promise> - : Promise | undefined>; - export function getEntry< - C extends keyof DataEntryMap, - E extends keyof DataEntryMap[C] | (string & {}), - >( - collection: C, - id: E, - ): E extends keyof DataEntryMap[C] - ? string extends keyof DataEntryMap[C] - ? Promise | undefined - : Promise - : Promise | undefined>; - export function getLiveEntry( - collection: C, - filter: string | LiveLoaderEntryFilterType, - ): Promise, LiveLoaderErrorType>>; - - /** Resolve an array of entry references from the same collection */ - export function getEntries( - entries: ReferenceContentEntry>[], - ): Promise[]>; - export function getEntries( - entries: ReferenceDataEntry[], - ): Promise[]>; - - export function render( - entry: AnyEntryMap[C][string], - ): Promise; - - export function reference( - collection: C, - ): import('astro/zod').ZodEffects< - import('astro/zod').ZodString, - C extends keyof ContentEntryMap - ? ReferenceContentEntry> - : ReferenceDataEntry - >; - // Allow generic `string` to avoid excessive type errors in the config - // if `dev` is not running to update as you edit. - // Invalid collection names will be caught at build time. - export function reference( - collection: C, - ): import('astro/zod').ZodEffects; - - type ReturnTypeOrOriginal = T extends (...args: any[]) => infer R ? R : T; - type InferEntrySchema = import('astro/zod').infer< - ReturnTypeOrOriginal['schema']> - >; - - type ContentEntryMap = { - - }; - - type DataEntryMap = { - - }; - - type AnyEntryMap = ContentEntryMap & DataEntryMap; - - type ExtractLoaderTypes = T extends import('astro/loaders').LiveLoader< - infer TData, - infer TEntryFilter, - infer TCollectionFilter, - infer TError - > - ? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError } - : { data: never; entryFilter: never; collectionFilter: never; error: never }; - type ExtractDataType = ExtractLoaderTypes['data']; - type ExtractEntryFilterType = ExtractLoaderTypes['entryFilter']; - type ExtractCollectionFilterType = ExtractLoaderTypes['collectionFilter']; - type ExtractErrorType = ExtractLoaderTypes['error']; - - type LiveLoaderDataType = - LiveContentConfig['collections'][C]['schema'] extends undefined - ? ExtractDataType - : import('astro/zod').infer< - Exclude - >; - type LiveLoaderEntryFilterType = - ExtractEntryFilterType; - type LiveLoaderCollectionFilterType = - ExtractCollectionFilterType; - type LiveLoaderErrorType = ExtractErrorType< - LiveContentConfig['collections'][C]['loader'] - >; - - export type ContentConfig = typeof import("../src/content.config.mjs"); - export type LiveContentConfig = never; -} - -``` - ---- - -## .astro/types.d.ts - -```ts -/// -/// -``` - ---- - ## .env.example ```example diff --git a/skills/integration/integration-astro-view-transitions/references/amplitude-quickstart.md b/skills/integration/integration-astro-view-transitions/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-astro-view-transitions/references/amplitude-quickstart.md +++ b/skills/integration/integration-astro-view-transitions/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-astro-view-transitions/references/browser-sdk-2.md b/skills/integration/integration-astro-view-transitions/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-astro-view-transitions/references/browser-sdk-2.md +++ b/skills/integration/integration-astro-view-transitions/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-astro-view-transitions/references/browser-unified-sdk.md b/skills/integration/integration-astro-view-transitions/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-astro-view-transitions/references/browser-unified-sdk.md +++ b/skills/integration/integration-astro-view-transitions/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-django/SKILL.md b/skills/integration/integration-django/SKILL.md index 05d8871a9..0835ae727 100644 --- a/skills/integration/integration-django/SKILL.md +++ b/skills/integration/integration-django/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Django applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Django example project code -- `references/python.md` - Amplitude documentation for Python +- `references/python.md` - Python - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-django/references/amplitude-quickstart.md b/skills/integration/integration-django/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-django/references/amplitude-quickstart.md +++ b/skills/integration/integration-django/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-django/references/python.md b/skills/integration/integration-django/references/python.md index 324f2e306..5efc98b78 100644 --- a/skills/integration/integration-django/references/python.md +++ b/skills/integration/integration-django/references/python.md @@ -1,1424 +1,43 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Python | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[Python](/docs/sdks/analytics/python) -
- -
-
-
-
- -
-
-
-

Python

- -
-
- -
- current - -

- - - - Python SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/python.svg) Python SDK](/docs/sdks/analytics-sdks/python/python-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Python) +- [Releases](https://github.com/amplitude/Amplitude-Python/releases) +- [Ampli](/docs/sdks/analytics-sdks/python/ampli-for-python-sdk) \ No newline at end of file diff --git a/skills/integration/integration-expo/SKILL.md b/skills/integration/integration-expo/SKILL.md index 54fdfb77b..e2fd69e8d 100644 --- a/skills/integration/integration-expo/SKILL.md +++ b/skills/integration/integration-expo/SKILL.md @@ -16,10 +16,10 @@ This skill helps you add Amplitude analytics to Expo applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files diff --git a/skills/integration/integration-expo/references/amplitude-quickstart.md b/skills/integration/integration-expo/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-expo/references/amplitude-quickstart.md +++ b/skills/integration/integration-expo/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-expo/references/react-native-sdk.md b/skills/integration/integration-expo/references/react-native-sdk.md index 78e53798b..2e257498c 100644 --- a/skills/integration/integration-expo/references/react-native-sdk.md +++ b/skills/integration/integration-expo/references/react-native-sdk.md @@ -1,1235 +1,60 @@ - +The React Native SDK lets you send events to Amplitude. - - - - - - +## React Native support - - - - - - - - - React Native SDK | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Gradle +Android Gradle Plugin -
-
+\>= 1.4.0 -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

React Native SDK

-
-
- - - - - - - - -

The React Native SDK lets you send events to Amplitude.

-

-

React Native support

-Because React-Native doesn't provide stable release versioning, ensuring backward compatibility is challenging. Additionally, React-Native itself isn't backward compatible and may introduce breaking changes across different versions. Check the React-Native compatibility list for more details. Amplitude supports only the latest version of React-Native.
-

-

Compatibility matrix

-

The following matrix lists the support for Amplitude React Native SDK version for different versions of React Native and React Native CLI.

- - - - - - - - - - - - - - - - - - - - - - - -
@amplitude/analytics-react-nativereact-nativeGradleAndroid Gradle Plugin
>= 1.4.0>= 0.687.5.1+7.2.1+
>= 1.0.0, <= 1.3.6>= 0.61, <= 0.703.5.3+3.5.3+
-

Learn more about the Android Gradle Plugin compatibility.

-

Install the SDK

-

To get started with using Amplitude React Native SDK, install the package to your project with npm. You must also install @react-native-async-storage/async-storage for the SDK to work as expected.

-

-

Web and Expo support

-This SDK can be used for react-native apps built for web or built using Expo (Expo Go not yet supported).
-

-

-

-
-
- -

-
npm install @amplitude/analytics-react-native
-npm install @react-native-async-storage/async-storage
-
-

-

-

-
yarn add @amplitude/analytics-react-native
-yarn add @react-native-async-storage/async-storage
-
-

-

-

-
expo install @amplitude/analytics-react-native
-expo install @react-native-async-storage/async-storage
-
-

-

-

-

Install the native modules to run the SDK on iOS.

-
cd ios
+\>= 0.68
+
+7.5.1+
+
+7.2.1+
+
+\>= 1.0.0, <= 1.3.6
+
+\>= 0.61, <= 0.70
+
+3.5.3+
+
+3.5.3+
+
+Learn more about the Android [Gradle Plugin compatibility](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle).
+
+## Install the SDK[](#install-the-sdk "Permalink")
+
+To get started with using Amplitude React Native SDK, install the package to your project with npm. You must also install `@react-native-async-storage/async-storage` for the SDK to work as expected.
+
+## Web and Expo support
+
+This SDK can be used for react-native apps built for web or built using [Expo](https://expo.dev/) (Expo Go not yet supported).
+
+Install the native modules to run the SDK on iOS.
+
+```bash
+cd ios
 pod install
-
-

Initialize the SDK

-

Initialization is necessary before any instrumentation is done. The API key for your Amplitude project is required. Optionally, a user ID and config object can be passed in this call. The SDK can be used anywhere after it's initialized anywhere in an application.

-
import { init } from '@amplitude/analytics-react-native';
+```
+
+## Initialize the SDK[](#initialize-the-sdk "Permalink")
+
+Initialization is necessary before any instrumentation is done. The API key for your Amplitude project is required. Optionally, a user ID and config object can be passed in this call. The SDK can be used anywhere after it's initialized anywhere in an application.
+
+```ts
+import { init } from '@amplitude/analytics-react-native';
 
 // Option 1, initialize with API_KEY only
 init(API_KEY);
@@ -1241,172 +66,20 @@ init(API_KEY, 'user@amplitude.com');
 init(API_KEY, 'user@amplitude.com', {
   disableCookies: true, // Disables the use of browser cookies
 });
-
-

Configure the SDK

-

-

Web vs. mobile

-The configuration of the SDK is shared across web and mobile platforms. However, many of these options simply don't apply when running the SDK on native platforms (for example iOS, Android). For example, when the SDK is run on web, the identity is stored in the browser cookie by default, whereas on native platforms identity is stored in async storage.
-

-

- Configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events that are batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to retryable errors.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level.LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class from the Logger to emit log messages to desired destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
deviceIdstring. Sets an identifier for the device running your application.UUID()
cookieExpirationnumber. Sets expiration of cookies created in days.365 days
cookieSameSitestring. Sets SameSite property of cookies created.Lax
cookieSecureboolean. Sets Secure property of cookies created.false
cookieStorageStorage<UserSession>. Sets a custom implementation of Storage<UserSession> to persist user identity.MemoryStorage<UserSession>
cookieUpgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
disableCookiesboolean. Sets permission to use cookies. If value is true, localStorage API is used to persist user identity.The cookies is enable by default.
domainstring. Sets the domain property of cookies created.undefined
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
userIdnumber. Sets an identifier for the user being tracked. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of additional properties. Please refer to Optional tracking section for more information.Enable all tracking options by default.
storageProviderStorage<Event[]>. Implements a custom storageProvider class from Storage.MemoryStorage
trackingSessionEventsboolean. Whether to automatically log start and end session events corresponding to the start and end of a user's session.false
migrateLegacyDataboolean. Available in 1.3.4+. Whether to migrate maintenance SDK data (events, user/device ID).true
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. Every event logged by the track method is queued in memory. Events are flushed in batches in background. You can customize batch behavior with flushQueueSize and flushIntervalMillis. By default, the serverUrl will be https://api2.amplitude.com/2/httpapi. For customers who want to send large batches of data at a time, set useBatch to true to set setServerUrl to batch event upload API https://api2.amplitude.com/batch. Both the regular mode and the batch mode use the same events upload threshold and flush time intervals.

-
import * as amplitude from '@amplitude/analytics-react-native';
+```
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+## Web vs. mobile
+
+The configuration of the SDK is shared across web and mobile platforms. However, many of these options simply don't apply when running the SDK on native platforms (for example iOS, Android). For example, when the SDK is run on web, the identity is stored in the browser cookie by default, whereas on native platforms identity is stored in async storage.
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. Every event logged by the `track` method is queued in memory. Events are flushed in batches in background. You can customize batch behavior with `flushQueueSize` and `flushIntervalMillis`. By default, the serverUrl will be `https://api2.amplitude.com/2/httpapi`. For customers who want to send large batches of data at a time, set `useBatch` to `true` to set `setServerUrl` to batch event upload API `https://api2.amplitude.com/batch`. Both the regular mode and the batch mode use the same events upload threshold and flush time intervals.
+
+```ts
+import * as amplitude from '@amplitude/analytics-react-native';
 
 amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   // Events queued in memory will flush when number of events exceed upload threshold
@@ -1416,60 +89,81 @@ amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   // Default value is 10000 milliseconds
   flushIntervalMillis: 20000,
 });
-
-

EU data residency

-

You can configure the server zone when initializing the client for sending data to Amplitude's EU servers. The SDK sends data based on the server zone if it's set.

-

-

Note

-For EU data residency, the project must be set up inside Amplitude EU. You must initialize the SDK with the API key from Amplitude EU.
-

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+You can configure the server zone when initializing the client for sending data to Amplitude's EU servers. The SDK sends data based on the server zone if it's set.
+
+## Note
+
+For EU data residency, the project must be set up inside Amplitude EU. You must initialize the SDK with the API key from Amplitude EU.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   serverZone: 'EU',
 });
-
-

Debugging

-

You can control the level of logs printed to the developer console.

-
    -
  • 'None': Suppresses all log messages.
  • -
  • 'Error': Shows error messages only.
  • -
  • 'Warn': Shows error messages and warnings. This is the default value if logLevel isn't explicitly specified.
  • -
  • 'Verbose': Shows informative messages.
  • -
  • 'Debug': Shows error messages, warnings, and informative messages that may be useful for debugging, including the function context information for all SDK public method invocations. This logging mode is only suggested to be used in development phases.
  • -
-

Set the log level by configuring the logLevel with the level you want.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Debugging[](#debugging "Permalink")
+
+You can control the level of logs printed to the developer console.
+
+-   'None': Suppresses all log messages.
+-   'Error': Shows error messages only.
+-   'Warn': Shows error messages and warnings. This is the default value if `logLevel` isn't explicitly specified.
+-   'Verbose': Shows informative messages.
+-   'Debug': Shows error messages, warnings, and informative messages that may be useful for debugging, including the function context information for all SDK public method invocations. This logging mode is only suggested to be used in development phases.
+
+Set the log level by configuring the `logLevel` with the level you want.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

The default logger outputs extra function context information to the developer console when invoking any SDK public method, including:

-
    -
  • 'type': Category of this context, for example "invoke public method".
  • -
  • 'name': Name of invoked function, for example "track".
  • -
  • 'args': Arguments of the invoked function.
  • -
  • 'stacktrace': Stacktrace of the invoked function.
  • -
  • 'time': Start and end timestamp of the function invocation.
  • -
  • 'states': Useful internal states snapshot before and after the function invocation.
  • -
-

Track events

-

-

Note

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, the upload may be rejected with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

Events represent how users interact with your application. For example, "Button Clicked" may be an action you want to note.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+The default logger outputs extra function context information to the developer console when invoking any SDK public method, including:
+
+-   'type': Category of this context, for example "invoke public method".
+-   'name': Name of invoked function, for example "track".
+-   'args': Arguments of the invoked function.
+-   'stacktrace': Stacktrace of the invoked function.
+-   'time': Start and end timestamp of the function invocation.
+-   'states': Useful internal states snapshot before and after the function invocation.
+
+## Track events[](#track-events "Permalink")
+
+## Note
+
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, the upload may be rejected with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+Events represent how users interact with your application. For example, "Button Clicked" may be an action you want to note.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 // Track a basic event
 track('Button Clicked');
@@ -1479,10 +173,14 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 track('Button Clicked', eventProperties);
-
-

Track events to multiple projects

-

If you need to log events to multiple Amplitude projects, you'll need to create separate instances for each Amplitude project. Then, pass the instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKeys, userIds, deviceIds, and settings.

-
import * as amplitude from '@amplitude/analytics-react-native';
+```
+
+### Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+If you need to log events to multiple Amplitude projects, you'll need to create separate instances for each Amplitude project. Then, pass the instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKeys`, `userIds`, `deviceIds`, and settings.
+
+```ts
+import * as amplitude from '@amplitude/analytics-react-native';
 
 const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
@@ -1491,138 +189,200 @@ const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties help you understand your users at the time they performed some action within your app such as their device details, their preferences, or language.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. The operations are declared through a provided Identify interface. You can chain multiple operations together in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Note

-If the Identify call is sent after the event, the results of operations will be visible immediately in the dashboard user’s profile area, but it won't appear in chart result until another event is sent after the Identify call. The identify call only affects events going forward. More details here.
-

-

Identify

-

The Identify object provides controls over setting user properties. An Identify object must first be instantiated, then Identify methods can be called on it, and finally the client will make a call with the Identify object.

-
import { identify, Identify } from '@amplitude/analytics-react-native';
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties help you understand your users at the time they performed some action within your app such as their device details, their preferences, or language.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. The operations are declared through a provided Identify interface. You can chain multiple operations together in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Note
+
+If the Identify call is sent after the event, the results of operations will be visible immediately in the dashboard user’s profile area, but it won't appear in chart result until another event is sent after the Identify call. The identify call only affects events going forward. More details [here](/docs/data/user-properties-and-events).
+
+### Identify[](#identify "Permalink")
+
+The Identify object provides controls over setting user properties. An Identify object must first be instantiated, then Identify methods can be called on it, and finally the client will make a call with the Identify object.
+
+```ts
+import { identify, Identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identify(identifyObj);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.set('location', 'LAX');
 
 identify(identifyObj);
-
-

Identify.setOnce

-

This method sets the value of a user property only once. Subsequent calls using setOnce() will be ignored. For example, you can set an initial login method for a user and since only the initial value is tracked, setOnce() ignores subsequent calls.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only once. Subsequent calls using setOnce() will be ignored. For example, you can set an initial login method for a user and since only the initial value is tracked, setOnce() ignores subsequent calls.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.setOnce('initial-location', 'SFO');
 
 identify(identifyObj);
-
-

Identify.add

-

This method increments a user property by some numerical value. If the user property doesn't have a value set yet, it will be initialized to 0 before being incremented. For example, you can track a user's travel count.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by some numerical value. If the user property doesn't have a value set yet, it will be initialized to 0 before being incremented. For example, you can track a user's travel count.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.add('travel-count', 1);
 
 identify(identifyObj);
-
-

Arrays in user properties

-

Arrays can be used as user properties. You can directly set arrays or use prepend, append, preInsert and postInsert to generate an array.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Arrays can be used as user properties. You can directly set arrays or use `prepend`, `append`, `preInsert` and `postInsert` to generate an array.
+
+#### `Identify.prepend`[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.prepend('visited-locations', 'LAX');
 
 identify(identifyObj);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### `Identify.append`[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.append('visited-locations', 'SFO');
 
 identify(identifyObj);
-
-

Identify.preInsert

-

This method pre-inserts a value or values to a user property if it doesn't exist in the user property yet. Pre-insert means inserting the value at the beginning of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are pre-inserted. If the user property has an existing value, it will be no operation.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### `Identify.preInsert`[](#identifypreinsert "Permalink")
+
+This method pre-inserts a value or values to a user property if it doesn't exist in the user property yet. Pre-insert means inserting the value at the beginning of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are pre-inserted. If the user property has an existing value, it will be no operation.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.preInsert('unique-locations', 'LAX');
 
 identify(identifyObj);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the value at the end of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are post-inserted. If the user property has an existing value, it will be no operation.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the value at the end of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are post-inserted. If the user property has an existing value, it will be no operation.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.postInsert('unique-locations', 'SFO');
 
 identify(identifyObj);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the item doesn't exist in the user property, it's a no-op.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the item doesn't exist in the user property, it's a no-op.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.remove('unique-locations', 'JFK')
 
 identify(identifyObj);
-
-

Identify.clearAll

-

This method removes all user properties from a user. Use clearAll with care because the operation is irreversible.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from a user. Use `clearAll` with care because the operation is irreversible.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.clearAll();
 
 identify(identifyObj);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to indicate that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName would be '15'.

-
import { setGroup } from '@amplitude/analytics-react-native';
+```
+
+### User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's groupType, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to indicate that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` would be '15'.
+
+```ts
+import { setGroup } from '@amplitude/analytics-react-native';
 
 // set group with single group name
 setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'tennis' and 'soccer', then the groupName would be '["tennis", "soccer"]'.

-
import { setGroup } from '@amplitude/analytics-react-native';
+```
+
+If Joe is in 'sport' 'tennis' and 'soccer', then the `groupName` would be '\["tennis", "soccer"\]'.
+
+```ts
+import { setGroup } from '@amplitude/analytics-react-native';
 
 // set group with multiple group names
 setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

You can also set event-level groups by passing an Event Object with groups to track. With event-level groups, the group designation applies only to the specific event being logged, and doesn't persist on the user unless you explicitly set it with setGroup.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+You can also set **event-level groups** by passing an `Event` Object with `groups` to `track`. With event-level groups, the group designation applies only to the specific event being logged, and doesn't persist on the user unless you explicitly set it with `setGroup`.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 });
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that will be applied to the group.

-
import { Identify, groupIdentify } from '@amplitude/analytics-react-native';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that will be applied to the group.
+
+```ts
+import { Identify, groupIdentify } from '@amplitude/analytics-react-native';
 
 const groupType = 'plan';
 const groupName = 'enterprise';
@@ -1630,11 +390,16 @@ const event = new Identify()
 event.set('key1', 'value1');
 
 groupIdentify(groupType, groupName, identify);
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances will store each revenue transaction and allow you to define several special revenue properties (such as "revenueType", "productIdentifier", etc.) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

To track revenue from a user, call revenue each time a user generates revenue. For example, 3 units of a product were purchased at $3.99.

-
import { Revenue, revenue } from '@amplitude/analytics-react-native';
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances will store each revenue transaction and allow you to define several special revenue properties (such as "revenueType", "productIdentifier", etc.) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+To track revenue from a user, call revenue each time a user generates revenue. For example, 3 units of a product were purchased at $3.99.
+
+```ts
+import { Revenue, revenue } from '@amplitude/analytics-react-native';
 
 const event = new Revenue()
   .setProductId('com.company.productId')
@@ -1642,156 +407,186 @@ const event = new Revenue()
   .setQuantity(3);
 
 revenue(event);
-
-

Revenue interface

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
product_idOptional. String. An identifier for the product. Amplitude recommends something like the Google Play Store product ID. Defaults to null.
quantityRequired. Int. The quantity of products purchased. revenue = quantity * price. Defaults to 1
priceRequired. Double. The price of the products purchased, and this can be negative. revenue = quantity * price. Defaults to null.
revenue_typeOptional, but required for revenue verification. String. The revenue type (for example tax, refund, income). Defaults to null.
receiptOptional. String. The receipt identifier of the revenue. Defaults to null
receipt_sigOptional, but required for revenue verification. String. The receipt signature of the revenue. Defaults to null.
propertiesOptional. JSONObject. An object of event properties to include in the revenue event. Defaults to null.
-

Flush the event buffer

-

The flush method triggers the client to send buffered events.

-
import { flush } from '@amplitude/analytics-react-native';
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Name
+
+Description
+
+`product_id`
+
+Optional. String. An identifier for the product. Amplitude recommends something like the Google Play Store product ID. Defaults to null.
+
+`quantity`
+
+Required. Int. The quantity of products purchased. `revenue = quantity * price`. Defaults to 1
+
+`price`
+
+Required. Double. The price of the products purchased, and this can be negative. `revenue = quantity * price`. Defaults to null.
+
+`revenue_type`
+
+Optional, but required for revenue verification. String. The revenue type (for example tax, refund, income). Defaults to null.
+
+`receipt`
+
+Optional. String. The receipt identifier of the revenue. Defaults to null
+
+`receipt_sig`
+
+Optional, but required for revenue verification. String. The receipt signature of the revenue. Defaults to null.
+
+`properties`
+
+Optional. JSONObject. An object of event properties to include in the revenue event. Defaults to null.
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events.
+
+```typescript
+import { flush } from '@amplitude/analytics-react-native';
 
 flush();
-
-

By default, flush is called automatically in an interval, if you want to flush the events altogether, you can control the async flow with the optional Promise interface, for example:

-
await init(AMPLITUDE_API_KEY).promise;
+```
+
+By default, `flush` is called automatically in an interval, if you want to flush the events altogether, you can control the async flow with the optional Promise interface, for example:
+
+```typescript
+await init(AMPLITUDE_API_KEY).promise;
 track('Button Clicked');
 await flush().promise;
-
-

Custom user ID

-

If your app has its login system that you want to track users with, you can call setUserId at any time.

-

TypeScript

-
import { setUserId } from '@amplitude/analytics-react-native';
+```
+
+## Custom user ID[](#custom-user-id "Permalink")
+
+If your app has its login system that you want to track users with, you can call `setUserId` at any time.
+
+TypeScript
+
+```ts
+import { setUserId } from '@amplitude/analytics-react-native';
 
 setUserId('user@amplitude.com');
-
-

You can also assign the User ID as an argument to the init call.

-
import { init } from '@amplitude/analytics-react-native';
+```
+
+You can also assign the User ID as an argument to the init call.
+
+```ts
+import { init } from '@amplitude/analytics-react-native';
 
 init(API_KEY, 'user@amplitude.com');
-
-

Custom session ID

-

You can assign a new Session ID using setSessionId. When setting a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-

TypeScript

-
import { setSessionId } from '@amplitude/analytics-react-native';
+```
+
+## Custom session ID[](#custom-session-id "Permalink")
+
+You can assign a new Session ID using `setSessionId`. When setting a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+TypeScript
+
+```ts
+import { setSessionId } from '@amplitude/analytics-react-native';
 
 setSessionId(Date.now());
-
-

Custom device ID

-

If your app has its login system that you want to track users with, you can call setUserId at any time.

-

You can assign a new device ID using deviceId. When setting a custom device ID, make sure the value is sufficiently unique. A UUID is recommended.

-
import { setDeviceId } from '@amplitude/analytics-react-native';
+```
+
+## Custom device ID[](#custom-device-id "Permalink")
+
+If your app has its login system that you want to track users with, you can call `setUserId` at any time.
+
+You can assign a new device ID using `deviceId`. When setting a custom device ID, make sure the value is sufficiently unique. A UUID is recommended.
+
+```ts
+import { setDeviceId } from '@amplitude/analytics-react-native';
 const { uuid } = require('uuidv4');
 
 setDeviceId(uuid());
-
-

Reset when a user logs out

-

reset is a shortcut to anonymize users after they log out, by:

-
    -
  • setting userId to undefined
  • -
  • setting deviceId to a new UUID value
  • -
-

With an undefined userId and a completely new deviceId, the current user would appear as a brand new user in dashboard.

-
import { reset } from '@amplitude/analytics-react-native';
+```
+
+## Reset when a user logs out[](#reset-when-a-user-logs-out "Permalink")
+
+`reset` is a shortcut to anonymize users after they log out, by:
+
+-   setting `userId` to `undefined`
+-   setting `deviceId` to a new UUID value
+
+With an undefined `userId` and a completely new `deviceId`, the current user would appear as a brand new user in dashboard.
+
+```ts
+import { reset } from '@amplitude/analytics-react-native';
 
 reset();
-
-

Opt users out of tracking

-

You can turn off logging for a given user by setting setOptOut to true.

-
import { setOptOut } from '@amplitude/analytics-react-native';
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+You can turn off logging for a given user by setting `setOptOut` to `true`.
+
+```ts
+import { setOptOut } from '@amplitude/analytics-react-native';
 
 setOptOut(true);
-
-

No events are saved or sent to the server while setOptOut is enabled, and the setting persists across page loads.

-

Re-enable logging by setting setOptOut to false.

-
import { setOptOut } from '@amplitude/analytics-react-native';
+```
+
+No events are saved or sent to the server while `setOptOut` is enabled, and the setting persists across page loads.
+
+Re-enable logging by setting `setOptOut` to `false`.
+
+```ts
+import { setOptOut } from '@amplitude/analytics-react-native';
 
 setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
adidtrue
carriertrue
deviceManufacturertrue
deviceModeltrue
ipAddresstrue
languagetrue
osNametrue
osVersiontrue
platformtrue
-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`adid`
+
+`true`
+
+`carrier`
+
+`true`
+
+`deviceManufacturer`
+
+`true`
+
+`deviceModel`
+
+`true`
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
+
+`osName`
+
+`true`
+
+`osVersion`
+
+`true`
+
+`platform`
+
+`true`
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   trackingOptions: {
     adid: false,
     appSetId: false,
@@ -1806,46 +601,69 @@ setOptOut(false);
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 // Using async/await
 const results = await track('Button Clicked').promise;
 result.event; // {...} (The final event object sent to Amplitude)
 result.code; // 200 (The HTTP response status code of the request.
-result.message; // "Event tracked successfully" (The response message)
+result.message; // "Event tracked successfully" (The response message)
 
 // Using promises
-track('Button Clicked').promise.then((result) => {
+track('Button Clicked').promise.then((result) => {
   result.event; // {...} (The final event object sent to Amplitude)
   result.code; // 200 (The HTTP response status code of the request.
-  result.message; // "Event tracked successfully" (The response message)
+  result.message; // "Event tracked successfully" (The response message)
 });
-
-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment type) or sending to third-party APIs (destination type). A plugin is an object with methods setup() and execute().

-

For Session Replay integration with Segment, review the Session Replay React Native Segment Integration guide.

-

add

-

The add method adds a plugin to Amplitude. Plugins can help processing and sending events.

-
import { add } from '@amplitude/analytics-react-native';
+```
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment type) or sending to third-party APIs (destination type). A plugin is an object with methods `setup()` and `execute()`.
+
+For Session Replay integration with Segment, review the [Session Replay React Native Segment Integration](/docs/session-replay/session-replay-react-native-segment-integration) guide.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude. Plugins can help processing and sending events.
+
+```typescript
+import { add } from '@amplitude/analytics-react-native';
 
 add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
import { remove } from '@amplitude/analytics-react-native';
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```typescript
+import { remove } from '@amplitude/analytics-react-native';
 
 remove(plugin.name);
-
-

Plugin setup

-

This method contains logic for preparing the plugin for use and has config as a parameter. The expected return value is undefined. A typical use for this method, is to copy configuration from config or instantiate plugin dependencies. This method is called when the plugin is registered to the client via client.add().

-

Plugin.execute

-

This method contains the logic for processing events and has event as parameter. If used as enrichment type plugin, the expected return value is the modified/enriched event; while if used as a destination type plugin, the expected return value is a map with keys: event (BaseEvent), code (number), and message (string). This method is called for each event, including Identify, GroupIdentify and Revenue events, that's instrumented using the client interface.

-

Enrichment type plugin example

-

Here's an example of a plugin that modifies each event that's instrumented by adding an increment integer to event_id property of an event starting from 100.

-
import { init, add } from '@amplitude/analytics-react-native';
+```
+
+### Plugin setup[](#plugin-setup "Permalink")
+
+This method contains logic for preparing the plugin for use and has config as a parameter. The expected return value is undefined. A typical use for this method, is to copy configuration from config or instantiate plugin dependencies. This method is called when the plugin is registered to the client via `client.add()`.
+
+### Plugin.execute[](#pluginexecute "Permalink")
+
+This method contains the logic for processing events and has event as parameter. If used as enrichment type plugin, the expected return value is the modified/enriched event; while if used as a destination type plugin, the expected return value is a map with keys: `event` (BaseEvent), `code` (number), and `message` (string). This method is called for each event, including Identify, GroupIdentify and Revenue events, that's instrumented using the client interface.
+
+### Enrichment type plugin example[](#enrichment-type-plugin-example "Permalink")
+
+Here's an example of a plugin that modifies each event that's instrumented by adding an increment integer to `event_id` property of an event starting from 100.
+
+```ts
+import { init, add } from '@amplitude/analytics-react-native';
 import { ReactNativeConfig, EnrichmentPlugin, Event, PluginType } from '@amplitude/analytics-types';
 
 export class AddEventIdPlugin implements EnrichmentPlugin {
@@ -1858,7 +676,7 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
    * setup() is called on plugin installation
    * example: client.add(new AddEventIdPlugin());
    */
-  async setup(config: ReactNativeConfig): Promise<undefined> {
+  async setup(config: ReactNativeConfig): Promise {
      this.config = config;
      return;
   }
@@ -1867,7 +685,7 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
    * execute() is called on each event instrumented
    * example: client.track('New Event');
    */
-  async execute(event: Event): Promise<Event> {
+  async execute(event: Event): Promise {
     event.event_id = this.currentId++;
     return event;
   }
@@ -1875,10 +693,14 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
 
 init('API_KEY');
 add(new AddEventIdPlugin());
-
-

Destination type plugin example

-

Here's an example of a plugin that sends each instrumented event to a target server URL using your preferred HTTP client.

-
import { init, add } from '@amplitude/analytics-react-native';
+```
+
+### Destination type plugin example[](#destination-type-plugin-example "Permalink")
+
+Here's an example of a plugin that sends each instrumented event to a target server URL using your preferred HTTP client.
+
+```ts
+import { init, add } from '@amplitude/analytics-react-native';
 import { ReactNativeConfig, DestinationPlugin, Event, PluginType, Result } from '@amplitude/analytics-types';
 
 export class MyDestinationPlugin implements DestinationPlugin {
@@ -1895,7 +717,7 @@ export class MyDestinationPlugin implements DestinationPlugin {
    * setup() is called on plugin installation
    * example: client.add(new MyDestinationPlugin());
    */
-  async setup(config: ReactNativeConfig): Promise<undefined> {
+  async setup(config: ReactNativeConfig): Promise {
     this.config = config;
     return;
   }
@@ -1904,7 +726,7 @@ export class MyDestinationPlugin implements DestinationPlugin {
    * execute() is called on each event instrumented
    * example: client.track('New Event');
    */
-  async execute(event: Event): Promise<Result> {
+  async execute(event: Event): Promise {
     const payload = { key: 'secret', data: event };
     const response = await fetch(this.serverUrl, {
       method: 'POST',
@@ -1924,14 +746,19 @@ export class MyDestinationPlugin implements DestinationPlugin {
 
 init('API_KEY');
 add(new MyDestinationPlugin('https://custom.domain.com'));
-
-

Advanced topics

-

Custom HTTP client

-

You can provide an implementation of Transport interface to the transportProvider configuration option for customization purpose, for example, sending requests to your proxy server with customized HTTP request headers.

-
import { Transport } from '@amplitude/analytics-types';
+```
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Custom HTTP client[](#custom-http-client "Permalink")
+
+You can provide an implementation of `Transport` interface to the `transportProvider` configuration option for customization purpose, for example, sending requests to your proxy server with customized HTTP request headers.
+
+```ts
+import { Transport } from '@amplitude/analytics-types';
 
 class MyTransport implements Transport {
-  async send(serverUrl: string, payload: Payload): Promise<Response | null> {
+  async send(serverUrl: string, payload: Payload): Promise {
     // check example: https://github.com/amplitude/Amplitude-TypeScript/blob/main/packages/analytics-client-common/src/transports/fetch.ts
   }
 }
@@ -1939,882 +766,157 @@ class MyTransport implements Transport {
 amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   transportProvider: new MyTransport(),
 });
-
-

Location

-

The Amplitude ingestion servers resolve event location in the following order:

-
    -
  1. User-provided city, country, region
  2. -
  3. Resolved from location_lat and location_lng
  4. -
  5. Resolved from ip
  6. -
-

By default, location will be determined by the ip on the server side. If you want more provide more granular location you can set city, country and region individually, or set location_lat and location_lng which will then be resolved to city, country and region on the server.
-Amplitude doesn't set precise location in the SDK to avoid extra permissions that my not be needed by all customers.

-

To set fine grain location, you can use an enrichment Plugin. Here is an example of how to set location_lat and location_lng.

-

Disabling IP tracking with ipAddress: false in TrackingOptions prevents location from being resolved on the backend. In this case you may want to create a Plugin like above to set any relevant location information yourself.

-

Carrier

-

Carrier support works on Android, but Apple stopped supporting it in iOS 16. In earlier versions of iOS, we fetch carrier info using CTCarrier and serviceSubscriberCellularProviders which are deprecated with no replacement.

-

Advertising Identifiers

-

Different platforms have different advertising identifiers. Due to user privacy concerns, Amplitude does not automatically collect these identifiers. However, it is easy to enable them using the instructions below. It is important to note that some identifiers are no longer recommended for use by the platform providers. Read the notes below before deciding to enable them.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PlatformAdvertising IdentifierRecommendedNotes
AndroidAppSetIdYesAppSetId is a unique identifier for the app instance. It is reset when the app is reinstalled.
AndroidADIDNoADID is a unique identifier for the device. It is reset when the user opts out of personalized ads.
iOSIDFVYesIDFV is a unique identifier for the app instance. It is reset when the app is reinstalled.
iOSIDFANoIDFA is a unique identifier for the device. It is reset when the user opts out of personalized ads.
-

Android

-
App set ID
-

App set ID is a unique identifier for each app install on a device. App set ID is reset by the user manually when they uninstall the app, or after 13 months of not opening the app. Google designed this as a privacy-friendly alternative to Ad ID for users who want to opt out of stronger analytics.

-

To use App Set ID, follow these steps.

-
    -
  1. -

    Add play-services-appset as a dependency to the Android project of your app.

    -
    dependencies {
    -    implementation 'com.google.android.gms:play-services-appset:16.0.2'
    -}
    -
    -
  2. -
  3. -

    Enable trackingOptions.appSetId

    -
    amplitude.init(API_KEY, OPTIONAL_USER_ID, {
    -  trackingOptions: {
    -    appSetId: true,
    -  },
    -});
    -
    -
  4. -
-
Android Ad ID
-

Android Ad ID is a unique identifier for each device. Android Ad ID is reset by the user manually when they opt out of personalized ads.

-

To use Android Ad ID, follow these steps.

-
    -
  1. -

    Add play-services-ads-identifier as a dependency to the Android project of your app. More detailed setup is described in our latest Android SDK docs.

    -
    dependencies {
    -  implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
    -}
    -
    -
  2. -
-

Android Ad Id is enabled by default. To disable it, set trackingOptions.adId to false.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  trackingOptions: {
-    adId: false,
-  },
-});
-
-

iOS

-
IDFV
-

IDFV is a unique identifier for the app instance. It is reset when the app is reinstalled.

-

To enable IDFV on iOS devices set trackingOptions.idfv to true.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  trackingOptions: {
-    idfv: true,
-  },
-});
-
-
IDFA
-

-

Warning

-IDFA is no longer recommended. You should consider using IDFV instead when possible.
-

-

IDFA is a unique identifier for the device. It is reset when the user opts out of personalized ads.

-

The React Native SDK does not directly access the IDFA as it would require adding the AdSupport.framework to your app. Instead you can use an Enrichment Plugin to set the IDFA yourself.

-

Here is an example Plugin that sets the IDFA using a third-party library.

-

Over the air updates (OTA)

-

If you are using platform like Expo that supports OTA updates. It is important to know our SDK has both native and JS code. If you are using OTA updates, you will need to make sure the native code is updated as well. See Expo's documentation on publishing and runtime versions for more details.

-

Below are versions of the SDK with the native code changes:

- - - - - - - - - - - -
@amplitude/analytics-react-native
1.3.0
- - -
-
+``` - - +### Location[](#location "Permalink") - +##### IDFA[](#idfa "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

July 23rd, 2024

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +IDFA is no longer recommended. You should consider using IDFV instead when possible. - - - - - - - - - - +@amplitude/analytics-react-native - - \ No newline at end of file +[1.3.0](https://github.com/amplitude/Amplitude-TypeScript/releases/tag/%40amplitude%2Fanalytics-react-native%401.3.0) \ No newline at end of file diff --git a/skills/integration/integration-fastapi/SKILL.md b/skills/integration/integration-fastapi/SKILL.md index 919281c46..0f1d10c8c 100644 --- a/skills/integration/integration-fastapi/SKILL.md +++ b/skills/integration/integration-fastapi/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to FastAPI applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - FastAPI example project code -- `references/python.md` - Amplitude documentation for Python +- `references/python.md` - Python - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-fastapi/references/amplitude-quickstart.md b/skills/integration/integration-fastapi/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-fastapi/references/amplitude-quickstart.md +++ b/skills/integration/integration-fastapi/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-fastapi/references/python.md b/skills/integration/integration-fastapi/references/python.md index 324f2e306..5efc98b78 100644 --- a/skills/integration/integration-fastapi/references/python.md +++ b/skills/integration/integration-fastapi/references/python.md @@ -1,1424 +1,43 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Python | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[Python](/docs/sdks/analytics/python) -
- -
-
-
-
- -
-
-
-

Python

- -
-
- -
- current - -

- - - - Python SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/python.svg) Python SDK](/docs/sdks/analytics-sdks/python/python-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Python) +- [Releases](https://github.com/amplitude/Amplitude-Python/releases) +- [Ampli](/docs/sdks/analytics-sdks/python/ampli-for-python-sdk) \ No newline at end of file diff --git a/skills/integration/integration-flask/SKILL.md b/skills/integration/integration-flask/SKILL.md index c3c45689c..0a553426d 100644 --- a/skills/integration/integration-flask/SKILL.md +++ b/skills/integration/integration-flask/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Flask applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Flask example project code -- `references/python.md` - Amplitude documentation for Python +- `references/python.md` - Python - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-flask/references/amplitude-quickstart.md b/skills/integration/integration-flask/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-flask/references/amplitude-quickstart.md +++ b/skills/integration/integration-flask/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-flask/references/python.md b/skills/integration/integration-flask/references/python.md index 324f2e306..5efc98b78 100644 --- a/skills/integration/integration-flask/references/python.md +++ b/skills/integration/integration-flask/references/python.md @@ -1,1424 +1,43 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Python | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[Python](/docs/sdks/analytics/python) -
- -
-
-
-
- -
-
-
-

Python

- -
-
- -
- current - -

- - - - Python SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/python.svg) Python SDK](/docs/sdks/analytics-sdks/python/python-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Python) +- [Releases](https://github.com/amplitude/Amplitude-Python/releases) +- [Ampli](/docs/sdks/analytics-sdks/python/ampli-for-python-sdk) \ No newline at end of file diff --git a/skills/integration/integration-javascript_node/SKILL.md b/skills/integration/integration-javascript_node/SKILL.md index 9f1c59772..7d740be87 100644 --- a/skills/integration/integration-javascript_node/SKILL.md +++ b/skills/integration/integration-javascript_node/SKILL.md @@ -16,15 +16,15 @@ This skill helps you add Amplitude analytics to JavaScript Node applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - JavaScript Node example project code -- `references/node.md` - Amplitude documentation for Node +- `references/node.md` - Node.js - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-javascript_node/references/amplitude-quickstart.md b/skills/integration/integration-javascript_node/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-javascript_node/references/amplitude-quickstart.md +++ b/skills/integration/integration-javascript_node/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-javascript_node/references/node.md b/skills/integration/integration-javascript_node/references/node.md index 7075df9bc..85d696f9b 100644 --- a/skills/integration/integration-javascript_node/references/node.md +++ b/skills/integration/integration-javascript_node/references/node.md @@ -1,1457 +1,52 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Node.js | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[Node.js](/docs/sdks/analytics/node) -
- -
-
-
-
- -
-
-
-

Node.js

- -
-
- -
- current - -

- - - - - - Node.js SDK -

-
-
-
- -
- -
- maintenance - -

- - - - Node SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/js.svg) ![](/docs/assets/icons/ts.svg) Node.js SDK](/docs/sdks/analytics/node/node-js-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Node) +- [Releases](https://github.com/amplitude/Amplitude-Node/releases) +- [Ampli](/docs/sdks/analytics/node/node-js-ampli-wrapper) +- [Node.js SDK Migration Guide](/docs/sdks/analytics/node/node-js-sdk-migration-guide) \ No newline at end of file diff --git a/skills/integration/integration-javascript_web/SKILL.md b/skills/integration/integration-javascript_web/SKILL.md index e71b455a1..578084fa5 100644 --- a/skills/integration/integration-javascript_web/SKILL.md +++ b/skills/integration/integration-javascript_web/SKILL.md @@ -16,16 +16,16 @@ This skill helps you add Amplitude analytics to JavaScript Web applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - JavaScript Web example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-javascript_web/references/EXAMPLE.md b/skills/integration/integration-javascript_web/references/EXAMPLE.md index 8247b3980..51d432534 100644 --- a/skills/integration/integration-javascript_web/references/EXAMPLE.md +++ b/skills/integration/integration-javascript_web/references/EXAMPLE.md @@ -17,7 +17,7 @@ Use Amplitude’s [Browser Unified SDK (npm)](https://amplitude.com/docs/sdks/an The `experiment` block configures **Feature Experiment** (`@amplitude/experiment-js-client`). Amplitude’s [product support table](https://amplitude.com/docs/sdks/analytics/browser/browser-unified-sdk#product-support-by-installation-method) lists **Web Experiment** (`@amplitude/experiment-tag`, including the visual editor) for the Unified **CDN** script, not the Unified **npm** row. -For analytics from Node or other servers, use [`@amplitude/analytics-node`](https://www.npmjs.com/package/@amplitude/analytics-node) (for example the [javascript-node](../javascript-node) app), not `@amplitude/unified`. +For analytics from Node or other servers, use [`@amplitude/analytics-node`](https://www.npmjs.com/package/@amplitude/analytics-node) (for example the [javascript-node](../../integration-javascript_node/) app), not `@amplitude/unified`. ## Purpose diff --git a/skills/integration/integration-javascript_web/references/amplitude-quickstart.md b/skills/integration/integration-javascript_web/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-javascript_web/references/amplitude-quickstart.md +++ b/skills/integration/integration-javascript_web/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-javascript_web/references/basic-integration-1.1-edit.md b/skills/integration/integration-javascript_web/references/basic-integration-1.1-edit.md index 5e688334f..23055618c 100644 --- a/skills/integration/integration-javascript_web/references/basic-integration-1.1-edit.md +++ b/skills/integration/integration-javascript_web/references/basic-integration-1.1-edit.md @@ -7,7 +7,7 @@ For each of the files and events noted in .amplitude-events.json, make edits to For browser/frontend projects, use @amplitude/unified as the default SDK — it bundles Analytics, Session Replay, and Experiment in a single package. Only use @amplitude/analytics-browser if the project already has it installed. -For the Amplitude API key, follow the wizard's env-vs-inline rule (browser keys are public-by-design): use a `.env` / `.env.local` value ONLY if the project's framework has a built-in env-var convention that requires no build-config changes (Vite `VITE_*`, Next.js `NEXT_PUBLIC_*`, Create React App `REACT_APP_*`, Astro `PUBLIC_*`, Nuxt `runtimeConfig.public`, SvelteKit `$env/static/public`, Expo `app.config.js` extras). Otherwise INLINE the key directly in the SDK init call. Do NOT modify `webpack.config.js`, `rollup.config.*`, `vite.config.*`, `babel.config.*`, or any other build config to wire env vars into client code. When in doubt, inline. +Use environment variables for Amplitude keys. Do not hardcode Amplitude keys. Event names MUST use Title Case with spaces following the [Noun] + [Past-Tense Verb] pattern (e.g., "Button Clicked", "Sign Up Completed", "Cart Viewed"). Do NOT use snake_case or camelCase for event names. Property names should use snake_case (e.g., button_text, page_url). diff --git a/skills/integration/integration-javascript_web/references/browser-sdk-2.md b/skills/integration/integration-javascript_web/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-javascript_web/references/browser-sdk-2.md +++ b/skills/integration/integration-javascript_web/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-javascript_web/references/browser-unified-sdk.md b/skills/integration/integration-javascript_web/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-javascript_web/references/browser-unified-sdk.md +++ b/skills/integration/integration-javascript_web/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-laravel/SKILL.md b/skills/integration/integration-laravel/SKILL.md index b95fdbdfc..f91981fe2 100644 --- a/skills/integration/integration-laravel/SKILL.md +++ b/skills/integration/integration-laravel/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Laravel applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Laravel example project code -- `references/analytics.md` - Amplitude documentation for Analytics +- `references/analytics.md` - Amplitude analytics SDK catalog - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-laravel/references/amplitude-quickstart.md b/skills/integration/integration-laravel/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-laravel/references/amplitude-quickstart.md +++ b/skills/integration/integration-laravel/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-laravel/references/analytics.md b/skills/integration/integration-laravel/references/analytics.md index e73d554db..eafef6408 100644 --- a/skills/integration/integration-laravel/references/analytics.md +++ b/skills/integration/integration-laravel/references/analytics.md @@ -1,1778 +1,115 @@ - - - - - - - - - - - - - - - - - - Amplitude Analytics SDK Catalog | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
- - - - - - - -
-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - - - - \ No newline at end of file +[documentation](/docs) + +[Get Started](/docs/get-started) + +[Data](/docs/data) + +[Analytics](/docs/analytics) + +[Amplitude AI](/docs/amplitude-ai) + +[Session Replay](/docs/session-replay) + +[Guides and Surveys](/docs/guides-and-surveys) + +[AI Assistant](/docs/assistant) + +[Experiment](/docs/experiment-home) + +[Admin](/docs/admin) + +[Partners](/docs/partners) + +[FAQ](/docs/faq) + +[SDKs](/docs/sdks) + +/ + +[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) + +# Amplitude Analytics SDK Catalog + +[ + +Android + + + +](/docs/sdks/analytics/android)[ + +Browser + + + +](/docs/sdks/analytics/browser)[ + +Flutter + + + +](/docs/sdks/analytics/flutter)[ + +Go + + + +](/docs/sdks/analytics/go)[ + +iOS + + + +](/docs/sdks/analytics/ios)[ + +JRE Java + + + +](/docs/sdks/analytics/java)[ + +Node.js + + + +](/docs/sdks/analytics/node)[ + +Python + + + +](/docs/sdks/analytics/python)[ + +React Native + + + +](/docs/sdks/analytics/react-native)[ + +Unity + + + +](/docs/sdks/analytics/unity)[ + +Unreal Engine + + + +](/docs/sdks/analytics/unreal) + + + +Need help? [Contact Support](https://help.amplitude.com/hc/en-us/requests/new) + +Visit [Amplitude.com](https://www.amplitude.com) + +Have a look at the Amplitude [Blog](https://amplitude.com/blog) + +Learn more at [Amplitude Academy](https://academy.amplitude.com/) + +[](https://www.linkedin.com/company/amplitude-analytics)[](https://twitter.com/Amplitude_HQ)[](https://www.g2.com/products/amplitude-analytics/reviews) + +[Terms of Service](https://amplitude.com/terms) [Privacy Notice](https://amplitude.com/privacy) [Acceptable Use Policy](https://amplitude.com/aup) [Legal](https://amplitude.com/legal) + +© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc. \ No newline at end of file diff --git a/skills/integration/integration-nextjs-app-router/SKILL.md b/skills/integration/integration-nextjs-app-router/SKILL.md index 9a9cd7ead..14fef6a39 100644 --- a/skills/integration/integration-nextjs-app-router/SKILL.md +++ b/skills/integration/integration-nextjs-app-router/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Next.js App Router applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Next.js App Router example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-nextjs-app-router/references/EXAMPLE.md b/skills/integration/integration-nextjs-app-router/references/EXAMPLE.md index ef0dea5ff..180b3dbc6 100644 --- a/skills/integration/integration-nextjs-app-router/references/EXAMPLE.md +++ b/skills/integration/integration-nextjs-app-router/references/EXAMPLE.md @@ -176,20 +176,6 @@ void amplitude.initAll(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY!, { --- -## next-env.d.ts - -```ts -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. - -``` - ---- - ## next.config.ts ```ts diff --git a/skills/integration/integration-nextjs-app-router/references/amplitude-quickstart.md b/skills/integration/integration-nextjs-app-router/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-nextjs-app-router/references/amplitude-quickstart.md +++ b/skills/integration/integration-nextjs-app-router/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-nextjs-app-router/references/browser-sdk-2.md b/skills/integration/integration-nextjs-app-router/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-nextjs-app-router/references/browser-sdk-2.md +++ b/skills/integration/integration-nextjs-app-router/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-nextjs-app-router/references/browser-unified-sdk.md b/skills/integration/integration-nextjs-app-router/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-nextjs-app-router/references/browser-unified-sdk.md +++ b/skills/integration/integration-nextjs-app-router/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-nextjs-pages-router/SKILL.md b/skills/integration/integration-nextjs-pages-router/SKILL.md index 39b91b11f..4f3ed04b8 100644 --- a/skills/integration/integration-nextjs-pages-router/SKILL.md +++ b/skills/integration/integration-nextjs-pages-router/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Next.js Pages Router application Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Next.js Pages Router example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-nextjs-pages-router/references/EXAMPLE.md b/skills/integration/integration-nextjs-pages-router/references/EXAMPLE.md index e8aac5847..4f34d90f6 100644 --- a/skills/integration/integration-nextjs-pages-router/references/EXAMPLE.md +++ b/skills/integration/integration-nextjs-pages-router/references/EXAMPLE.md @@ -164,20 +164,6 @@ void amplitude.initAll(process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY!, { --- -## next-env.d.ts - -```ts -/// -/// -/// - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. - -``` - ---- - ## next.config.ts ```ts diff --git a/skills/integration/integration-nextjs-pages-router/references/amplitude-quickstart.md b/skills/integration/integration-nextjs-pages-router/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-nextjs-pages-router/references/amplitude-quickstart.md +++ b/skills/integration/integration-nextjs-pages-router/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-nextjs-pages-router/references/browser-sdk-2.md b/skills/integration/integration-nextjs-pages-router/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-nextjs-pages-router/references/browser-sdk-2.md +++ b/skills/integration/integration-nextjs-pages-router/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-nextjs-pages-router/references/browser-unified-sdk.md b/skills/integration/integration-nextjs-pages-router/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-nextjs-pages-router/references/browser-unified-sdk.md +++ b/skills/integration/integration-nextjs-pages-router/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-nuxt-3.6/SKILL.md b/skills/integration/integration-nuxt-3.6/SKILL.md index ede565af3..11038c72d 100644 --- a/skills/integration/integration-nuxt-3.6/SKILL.md +++ b/skills/integration/integration-nuxt-3.6/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Nuxt 3.6 applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Nuxt 3.6 example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-nuxt-3.6/references/EXAMPLE.md b/skills/integration/integration-nuxt-3.6/references/EXAMPLE.md index 80595af8d..2404623d5 100644 --- a/skills/integration/integration-nuxt-3.6/references/EXAMPLE.md +++ b/skills/integration/integration-nuxt-3.6/references/EXAMPLE.md @@ -190,7652 +190,6 @@ NUXT_PUBLIC_AMPLITUDE_API_KEY= --- -## .nuxt/app.config.mjs - -```mjs - -import { _replaceAppConfig } from '#app/config' -import { defuFn } from 'defu' - -const inlineConfig = { - "nuxt": {} -} - -// Vite - webpack is handled directly in #app/config -if (import.meta.hot) { - import.meta.hot.accept((newModule) => { - _replaceAppConfig(newModule.default) - }) -} - - - -export default /*@__PURE__*/ defuFn(inlineConfig) - -``` - ---- - -## .nuxt/components.d.ts - -```ts - -import type { DefineComponent, SlotsType } from 'vue' -type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T - -type HydrationStrategies = { - hydrateOnVisible?: IntersectionObserverInit | true - hydrateOnIdle?: number | true - hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true - hydrateOnMediaQuery?: string - hydrateAfter?: number - hydrateWhen?: boolean - hydrateNever?: true -} -type LazyComponent = DefineComponent void }> & T - - -export const Header: typeof import("../components/Header.vue").default -export const NuxtWelcome: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/welcome.vue").default -export const NuxtLayout: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-layout").default -export const NuxtErrorBoundary: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue").default -export const ClientOnly: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/client-only").default -export const DevOnly: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/dev-only").default -export const ServerPlaceholder: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/server-placeholder").default -export const NuxtLink: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-link").default -export const NuxtLoadingIndicator: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-loading-indicator").default -export const NuxtTime: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-time.vue").default -export const NuxtRouteAnnouncer: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-route-announcer").default -export const NuxtImg: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtImg -export const NuxtPicture: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtPicture -export const NuxtPage: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/pages/runtime/page").default -export const NoScript: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").NoScript -export const Link: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Link -export const Base: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Base -export const Title: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Title -export const Meta: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Meta -export const Style: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Style -export const Head: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Head -export const Html: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Html -export const Body: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Body -export const NuxtIsland: typeof import("../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-island").default -export const LazyHeader: LazyComponent -export const LazyNuxtWelcome: LazyComponent -export const LazyNuxtLayout: LazyComponent -export const LazyNuxtErrorBoundary: LazyComponent -export const LazyClientOnly: LazyComponent -export const LazyDevOnly: LazyComponent -export const LazyServerPlaceholder: LazyComponent -export const LazyNuxtLink: LazyComponent -export const LazyNuxtLoadingIndicator: LazyComponent -export const LazyNuxtTime: LazyComponent -export const LazyNuxtRouteAnnouncer: LazyComponent -export const LazyNuxtImg: LazyComponent -export const LazyNuxtPicture: LazyComponent -export const LazyNuxtPage: LazyComponent -export const LazyNoScript: LazyComponent -export const LazyLink: LazyComponent -export const LazyBase: LazyComponent -export const LazyTitle: LazyComponent -export const LazyMeta: LazyComponent -export const LazyStyle: LazyComponent -export const LazyHead: LazyComponent -export const LazyHtml: LazyComponent -export const LazyBody: LazyComponent -export const LazyNuxtIsland: LazyComponent - -export const componentNames: string[] - -``` - ---- - -## .nuxt/imports.d.ts - -```ts -export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel, useScriptRybbitAnalytics, useScriptDatabuddyAnalytics, useScriptRedditPixel, useScriptPayPal } from '#app/composables/script-stubs'; -export { isVue2, isVue3 } from 'vue-demi'; -export { defineNuxtLink } from '#app/components/nuxt-link'; -export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt'; -export { useAppConfig, updateAppConfig } from '#app/config'; -export { defineNuxtComponent } from '#app/composables/component'; -export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData } from '#app/composables/asyncData'; -export { useHydration } from '#app/composables/hydrate'; -export { callOnce } from '#app/composables/once'; -export { useState, clearNuxtState } from '#app/composables/state'; -export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error'; -export { useFetch, useLazyFetch } from '#app/composables/fetch'; -export { useCookie, refreshCookie } from '#app/composables/cookie'; -export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr'; -export { onNuxtReady } from '#app/composables/ready'; -export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload'; -export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router'; -export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload'; -export { useLoadingIndicator } from '#app/composables/loading-indicator'; -export { getAppManifest, getRouteRules } from '#app/composables/manifest'; -export { reloadNuxtApp } from '#app/composables/chunk'; -export { useRequestURL } from '#app/composables/url'; -export { usePreviewMode } from '#app/composables/preview'; -export { useRouteAnnouncer } from '#app/composables/route-announcer'; -export { useRuntimeHook } from '#app/composables/runtime-hook'; -export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head'; -export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router'; -export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, onWatcherCleanup, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'; -export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback'; -export { setInterval } from '#app/compat/interval'; -export { definePageMeta } from '../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/pages/runtime/composables'; -export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration'; -export { useAuth } from '../composables/useAuth'; -``` - ---- - -## .nuxt/nuxt.d.ts - -```ts -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// - -export {} - -``` - ---- - -## .nuxt/schema/nuxt.schema.d.ts - -```ts -export interface NuxtCustomSchema { - -} -export type CustomAppConfig = Exclude -type _CustomAppConfig = CustomAppConfig - -declare module '@nuxt/schema' { - interface NuxtConfig extends Omit {} - interface NuxtOptions extends Omit {} - interface CustomAppConfig extends _CustomAppConfig {} -} - -declare module 'nuxt/schema' { - interface NuxtConfig extends Omit {} - interface NuxtOptions extends Omit {} - interface CustomAppConfig extends _CustomAppConfig {} -} - -``` - ---- - -## .nuxt/types/app-defaults.d.ts - -```ts - -declare module 'nuxt/app/defaults' { - type DefaultAsyncDataErrorValue = null - type DefaultAsyncDataValue = null - type DefaultErrorValue = null - type DedupeOption = boolean | 'cancel' | 'defer' -} -``` - ---- - -## .nuxt/types/app.config.d.ts - -```ts - -import type { CustomAppConfig } from 'nuxt/schema' -import type { Defu } from 'defu' - - -declare const inlineConfig = { - "nuxt": {} -} -type ResolvedAppConfig = Defu -type IsAny = 0 extends 1 & T ? true : false - -type MergedAppConfig, Custom extends Record> = { - [K in keyof (Resolved & Custom)]: K extends keyof Custom - ? unknown extends Custom[K] - ? Resolved[K] - : IsAny extends true - ? Resolved[K] - : Custom[K] extends Record - ? Resolved[K] extends Record - ? MergedAppConfig - : Exclude - : Exclude - : Resolved[K] -} - -declare module 'nuxt/schema' { - interface AppConfig extends MergedAppConfig { } -} -declare module '@nuxt/schema' { - interface AppConfig extends MergedAppConfig { } -} - -``` - ---- - -## .nuxt/types/components.d.ts - -```ts - -import type { DefineComponent, SlotsType } from 'vue' -type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T - -type HydrationStrategies = { - hydrateOnVisible?: IntersectionObserverInit | true - hydrateOnIdle?: number | true - hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true - hydrateOnMediaQuery?: string - hydrateAfter?: number - hydrateWhen?: boolean - hydrateNever?: true -} -type LazyComponent = DefineComponent void }> & T - -interface _GlobalComponents { - 'Header': typeof import("../../components/Header.vue").default - 'NuxtWelcome': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/welcome.vue").default - 'NuxtLayout': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-layout").default - 'NuxtErrorBoundary': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue").default - 'ClientOnly': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/client-only").default - 'DevOnly': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/dev-only").default - 'ServerPlaceholder': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/server-placeholder").default - 'NuxtLink': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-link").default - 'NuxtLoadingIndicator': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-loading-indicator").default - 'NuxtTime': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-time.vue").default - 'NuxtRouteAnnouncer': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-route-announcer").default - 'NuxtImg': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtImg - 'NuxtPicture': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtPicture - 'NuxtPage': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/pages/runtime/page").default - 'NoScript': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").NoScript - 'Link': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Link - 'Base': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Base - 'Title': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Title - 'Meta': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Meta - 'Style': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Style - 'Head': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Head - 'Html': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Html - 'Body': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/head/runtime/components").Body - 'NuxtIsland': typeof import("../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-island").default - 'LazyHeader': LazyComponent - 'LazyNuxtWelcome': LazyComponent - 'LazyNuxtLayout': LazyComponent - 'LazyNuxtErrorBoundary': LazyComponent - 'LazyClientOnly': LazyComponent - 'LazyDevOnly': LazyComponent - 'LazyServerPlaceholder': LazyComponent - 'LazyNuxtLink': LazyComponent - 'LazyNuxtLoadingIndicator': LazyComponent - 'LazyNuxtTime': LazyComponent - 'LazyNuxtRouteAnnouncer': LazyComponent - 'LazyNuxtImg': LazyComponent - 'LazyNuxtPicture': LazyComponent - 'LazyNuxtPage': LazyComponent - 'LazyNoScript': LazyComponent - 'LazyLink': LazyComponent - 'LazyBase': LazyComponent - 'LazyTitle': LazyComponent - 'LazyMeta': LazyComponent - 'LazyStyle': LazyComponent - 'LazyHead': LazyComponent - 'LazyHtml': LazyComponent - 'LazyBody': LazyComponent - 'LazyNuxtIsland': LazyComponent -} - -declare module 'vue' { - export interface GlobalComponents extends _GlobalComponents { } -} - -export {} - -``` - ---- - -## .nuxt/types/imports.d.ts - -```ts -// Generated by auto imports -export {} -declare global { - const abortNavigation: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').abortNavigation - const addRouteMiddleware: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').addRouteMiddleware - const callOnce: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/once').callOnce - const cancelIdleCallback: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/compat/idle-callback').cancelIdleCallback - const clearError: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/error').clearError - const clearNuxtData: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/asyncData').clearNuxtData - const clearNuxtState: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/state').clearNuxtState - const computed: typeof import('vue').computed - const createError: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/error').createError - const customRef: typeof import('vue').customRef - const defineAppConfig: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').defineAppConfig - const defineAsyncComponent: typeof import('vue').defineAsyncComponent - const defineComponent: typeof import('vue').defineComponent - const defineLazyHydrationComponent: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/lazy-hydration').defineLazyHydrationComponent - const defineNuxtComponent: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/component').defineNuxtComponent - const defineNuxtLink: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/components/nuxt-link').defineNuxtLink - const defineNuxtPlugin: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').defineNuxtPlugin - const defineNuxtRouteMiddleware: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').defineNuxtRouteMiddleware - const definePageMeta: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/pages/runtime/composables').definePageMeta - const definePayloadPlugin: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').definePayloadPlugin - const definePayloadReducer: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/payload').definePayloadReducer - const definePayloadReviver: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/payload').definePayloadReviver - const effect: typeof import('vue').effect - const effectScope: typeof import('vue').effectScope - const getAppManifest: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/manifest').getAppManifest - const getCurrentInstance: typeof import('vue').getCurrentInstance - const getCurrentScope: typeof import('vue').getCurrentScope - const getRouteRules: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/manifest').getRouteRules - const h: typeof import('vue').h - const hasInjectionContext: typeof import('vue').hasInjectionContext - const inject: typeof import('vue').inject - const injectHead: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').injectHead - const isNuxtError: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/error').isNuxtError - const isPrerendered: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/payload').isPrerendered - const isProxy: typeof import('vue').isProxy - const isReactive: typeof import('vue').isReactive - const isReadonly: typeof import('vue').isReadonly - const isRef: typeof import('vue').isRef - const isShallow: typeof import('vue').isShallow - const isVue2: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/compat/vue-demi').isVue2 - const isVue3: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/compat/vue-demi').isVue3 - const loadPayload: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/payload').loadPayload - const markRaw: typeof import('vue').markRaw - const navigateTo: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').navigateTo - const nextTick: typeof import('vue').nextTick - const onActivated: typeof import('vue').onActivated - const onBeforeMount: typeof import('vue').onBeforeMount - const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave - const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate - const onBeforeUnmount: typeof import('vue').onBeforeUnmount - const onBeforeUpdate: typeof import('vue').onBeforeUpdate - const onDeactivated: typeof import('vue').onDeactivated - const onErrorCaptured: typeof import('vue').onErrorCaptured - const onMounted: typeof import('vue').onMounted - const onNuxtReady: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ready').onNuxtReady - const onPrehydrate: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').onPrehydrate - const onRenderTracked: typeof import('vue').onRenderTracked - const onRenderTriggered: typeof import('vue').onRenderTriggered - const onScopeDispose: typeof import('vue').onScopeDispose - const onServerPrefetch: typeof import('vue').onServerPrefetch - const onUnmounted: typeof import('vue').onUnmounted - const onUpdated: typeof import('vue').onUpdated - const onWatcherCleanup: typeof import('vue').onWatcherCleanup - const prefetchComponents: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/preload').prefetchComponents - const preloadComponents: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/preload').preloadComponents - const preloadPayload: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/payload').preloadPayload - const preloadRouteComponents: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/preload').preloadRouteComponents - const prerenderRoutes: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').prerenderRoutes - const provide: typeof import('vue').provide - const proxyRefs: typeof import('vue').proxyRefs - const reactive: typeof import('vue').reactive - const readonly: typeof import('vue').readonly - const ref: typeof import('vue').ref - const refreshCookie: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/cookie').refreshCookie - const refreshNuxtData: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/asyncData').refreshNuxtData - const reloadNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/chunk').reloadNuxtApp - const requestIdleCallback: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/compat/idle-callback').requestIdleCallback - const resolveComponent: typeof import('vue').resolveComponent - const setInterval: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/compat/interval').setInterval - const setPageLayout: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').setPageLayout - const setResponseStatus: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').setResponseStatus - const shallowReactive: typeof import('vue').shallowReactive - const shallowReadonly: typeof import('vue').shallowReadonly - const shallowRef: typeof import('vue').shallowRef - const showError: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/error').showError - const toRaw: typeof import('vue').toRaw - const toRef: typeof import('vue').toRef - const toRefs: typeof import('vue').toRefs - const toValue: typeof import('vue').toValue - const triggerRef: typeof import('vue').triggerRef - const tryUseNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').tryUseNuxtApp - const unref: typeof import('vue').unref - const updateAppConfig: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/config').updateAppConfig - const useAppConfig: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/config').useAppConfig - const useAsyncData: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/asyncData').useAsyncData - const useAttrs: typeof import('vue').useAttrs - const useAuth: typeof import('../../composables/useAuth').useAuth - const useCookie: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/cookie').useCookie - const useCssModule: typeof import('vue').useCssModule - const useCssVars: typeof import('vue').useCssVars - const useError: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/error').useError - const useFetch: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/fetch').useFetch - const useHead: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useHead - const useHeadSafe: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useHeadSafe - const useHydration: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/hydrate').useHydration - const useId: typeof import('vue').useId - const useLazyAsyncData: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/asyncData').useLazyAsyncData - const useLazyFetch: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/fetch').useLazyFetch - const useLink: typeof import('vue-router').useLink - const useLoadingIndicator: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/loading-indicator').useLoadingIndicator - const useModel: typeof import('vue').useModel - const useNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').useNuxtApp - const useNuxtData: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/asyncData').useNuxtData - const usePreviewMode: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/preview').usePreviewMode - const useRequestEvent: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').useRequestEvent - const useRequestFetch: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').useRequestFetch - const useRequestHeader: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').useRequestHeader - const useRequestHeaders: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').useRequestHeaders - const useRequestURL: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/url').useRequestURL - const useResponseHeader: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/ssr').useResponseHeader - const useRoute: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').useRoute - const useRouteAnnouncer: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/route-announcer').useRouteAnnouncer - const useRouter: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/router').useRouter - const useRuntimeConfig: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/nuxt').useRuntimeConfig - const useRuntimeHook: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/runtime-hook').useRuntimeHook - const useScript: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScript - const useScriptClarity: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptClarity - const useScriptCloudflareWebAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptCloudflareWebAnalytics - const useScriptCrisp: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptCrisp - const useScriptDatabuddyAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptDatabuddyAnalytics - const useScriptEventPage: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptEventPage - const useScriptFathomAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptFathomAnalytics - const useScriptGoogleAdsense: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleAdsense - const useScriptGoogleAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleAnalytics - const useScriptGoogleMaps: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleMaps - const useScriptGoogleTagManager: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleTagManager - const useScriptHotjar: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptHotjar - const useScriptIntercom: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptIntercom - const useScriptLemonSqueezy: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptLemonSqueezy - const useScriptMatomoAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptMatomoAnalytics - const useScriptMetaPixel: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptMetaPixel - const useScriptNpm: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptNpm - const useScriptPayPal: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptPayPal - const useScriptPlausibleAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptPlausibleAnalytics - const useScriptRedditPixel: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptRedditPixel - const useScriptRybbitAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptRybbitAnalytics - const useScriptSegment: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptSegment - const useScriptSnapchatPixel: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptSnapchatPixel - const useScriptStripe: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptStripe - const useScriptTriggerConsent: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptTriggerConsent - const useScriptTriggerElement: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptTriggerElement - const useScriptUmamiAnalytics: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptUmamiAnalytics - const useScriptVimeoPlayer: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptVimeoPlayer - const useScriptXPixel: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptXPixel - const useScriptYouTubePlayer: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/script-stubs').useScriptYouTubePlayer - const useSeoMeta: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useSeoMeta - const useServerHead: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useServerHead - const useServerHeadSafe: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useServerHeadSafe - const useServerSeoMeta: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/head').useServerSeoMeta - const useShadowRoot: typeof import('vue').useShadowRoot - const useSlots: typeof import('vue').useSlots - const useState: typeof import('../../node_modules/.pnpm/nuxt@3.21.0_@parcel+watcher@2.5.6_@types+node@20.19.31_@vue+compiler-sfc@3.5.27_cac@6.7_ecadb268bd75e3f36d2c1fc8da0a5024/node_modules/nuxt/dist/app/composables/state').useState - const useTemplateRef: typeof import('vue').useTemplateRef - const useTransitionState: typeof import('vue').useTransitionState - const watch: typeof import('vue').watch - const watchEffect: typeof import('vue').watchEffect - const watchPostEffect: typeof import('vue').watchPostEffect - const watchSyncEffect: typeof import('vue').watchSyncEffect - const withCtx: typeof import('vue').withCtx - const withDirectives: typeof import('vue').withDirectives - const withKeys: typeof import('vue').withKeys - const withMemo: typeof import('vue').withMemo - const withModifiers: typeof import('vue').withModifiers - const withScopeId: typeof import('vue').withScopeId -} -// for type re-export -declare global { - // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' - import('vue') -} -// for vue template auto import -import { UnwrapRef } from 'vue' -declare module 'vue' { - interface ComponentCustomProperties { - readonly abortNavigation: UnwrapRef - readonly addRouteMiddleware: UnwrapRef - readonly callOnce: UnwrapRef - readonly cancelIdleCallback: UnwrapRef - readonly clearError: UnwrapRef - readonly clearNuxtData: UnwrapRef - readonly clearNuxtState: UnwrapRef - readonly computed: UnwrapRef - readonly createError: UnwrapRef - readonly customRef: UnwrapRef - readonly defineAppConfig: UnwrapRef - readonly defineAsyncComponent: UnwrapRef - readonly defineComponent: UnwrapRef - readonly defineLazyHydrationComponent: UnwrapRef - readonly defineNuxtComponent: UnwrapRef - readonly defineNuxtLink: UnwrapRef - readonly defineNuxtPlugin: UnwrapRef - readonly defineNuxtRouteMiddleware: UnwrapRef - readonly definePageMeta: UnwrapRef - readonly definePayloadPlugin: UnwrapRef - readonly definePayloadReducer: UnwrapRef - readonly definePayloadReviver: UnwrapRef - readonly effect: UnwrapRef - readonly effectScope: UnwrapRef - readonly getAppManifest: UnwrapRef - readonly getCurrentInstance: UnwrapRef - readonly getCurrentScope: UnwrapRef - readonly getRouteRules: UnwrapRef - readonly h: UnwrapRef - readonly hasInjectionContext: UnwrapRef - readonly inject: UnwrapRef - readonly injectHead: UnwrapRef - readonly isNuxtError: UnwrapRef - readonly isPrerendered: UnwrapRef - readonly isProxy: UnwrapRef - readonly isReactive: UnwrapRef - readonly isReadonly: UnwrapRef - readonly isRef: UnwrapRef - readonly isShallow: UnwrapRef - readonly isVue2: UnwrapRef - readonly isVue3: UnwrapRef - readonly loadPayload: UnwrapRef - readonly markRaw: UnwrapRef - readonly navigateTo: UnwrapRef - readonly nextTick: UnwrapRef - readonly onActivated: UnwrapRef - readonly onBeforeMount: UnwrapRef - readonly onBeforeRouteLeave: UnwrapRef - readonly onBeforeRouteUpdate: UnwrapRef - readonly onBeforeUnmount: UnwrapRef - readonly onBeforeUpdate: UnwrapRef - readonly onDeactivated: UnwrapRef - readonly onErrorCaptured: UnwrapRef - readonly onMounted: UnwrapRef - readonly onNuxtReady: UnwrapRef - readonly onPrehydrate: UnwrapRef - readonly onRenderTracked: UnwrapRef - readonly onRenderTriggered: UnwrapRef - readonly onScopeDispose: UnwrapRef - readonly onServerPrefetch: UnwrapRef - readonly onUnmounted: UnwrapRef - readonly onUpdated: UnwrapRef - readonly onWatcherCleanup: UnwrapRef - readonly prefetchComponents: UnwrapRef - readonly preloadComponents: UnwrapRef - readonly preloadPayload: UnwrapRef - readonly preloadRouteComponents: UnwrapRef - readonly prerenderRoutes: UnwrapRef - readonly provide: UnwrapRef - readonly proxyRefs: UnwrapRef - readonly reactive: UnwrapRef - readonly readonly: UnwrapRef - readonly ref: UnwrapRef - readonly refreshCookie: UnwrapRef - readonly refreshNuxtData: UnwrapRef - readonly reloadNuxtApp: UnwrapRef - readonly requestIdleCallback: UnwrapRef - readonly resolveComponent: UnwrapRef - readonly setInterval: UnwrapRef - readonly setPageLayout: UnwrapRef - readonly setResponseStatus: UnwrapRef - readonly shallowReactive: UnwrapRef - readonly shallowReadonly: UnwrapRef - readonly shallowRef: UnwrapRef - readonly showError: UnwrapRef - readonly toRaw: UnwrapRef - readonly toRef: UnwrapRef - readonly toRefs: UnwrapRef - readonly toValue: UnwrapRef - readonly triggerRef: UnwrapRef - readonly tryUseNuxtApp: UnwrapRef - readonly unref: UnwrapRef - readonly updateAppConfig: UnwrapRef - readonly useAppConfig: UnwrapRef - readonly useAsyncData: UnwrapRef - readonly useAttrs: UnwrapRef - readonly useAuth: UnwrapRef - readonly useCookie: UnwrapRef - readonly useCssModule: UnwrapRef - readonly useCssVars: UnwrapRef - readonly useError: UnwrapRef - readonly useFetch: UnwrapRef - readonly useHead: UnwrapRef - readonly useHeadSafe: UnwrapRef - readonly useHydration: UnwrapRef - readonly useId: UnwrapRef - readonly useLazyAsyncData: UnwrapRef - readonly useLazyFetch: UnwrapRef - readonly useLink: UnwrapRef - readonly useLoadingIndicator: UnwrapRef - readonly useModel: UnwrapRef - readonly useNuxtApp: UnwrapRef - readonly useNuxtData: UnwrapRef - readonly usePreviewMode: UnwrapRef - readonly useRequestEvent: UnwrapRef - readonly useRequestFetch: UnwrapRef - readonly useRequestHeader: UnwrapRef - readonly useRequestHeaders: UnwrapRef - readonly useRequestURL: UnwrapRef - readonly useResponseHeader: UnwrapRef - readonly useRoute: UnwrapRef - readonly useRouteAnnouncer: UnwrapRef - readonly useRouter: UnwrapRef - readonly useRuntimeConfig: UnwrapRef - readonly useRuntimeHook: UnwrapRef - readonly useScript: UnwrapRef - readonly useScriptClarity: UnwrapRef - readonly useScriptCloudflareWebAnalytics: UnwrapRef - readonly useScriptCrisp: UnwrapRef - readonly useScriptDatabuddyAnalytics: UnwrapRef - readonly useScriptEventPage: UnwrapRef - readonly useScriptFathomAnalytics: UnwrapRef - readonly useScriptGoogleAdsense: UnwrapRef - readonly useScriptGoogleAnalytics: UnwrapRef - readonly useScriptGoogleMaps: UnwrapRef - readonly useScriptGoogleTagManager: UnwrapRef - readonly useScriptHotjar: UnwrapRef - readonly useScriptIntercom: UnwrapRef - readonly useScriptLemonSqueezy: UnwrapRef - readonly useScriptMatomoAnalytics: UnwrapRef - readonly useScriptMetaPixel: UnwrapRef - readonly useScriptNpm: UnwrapRef - readonly useScriptPayPal: UnwrapRef - readonly useScriptPlausibleAnalytics: UnwrapRef - readonly useScriptRedditPixel: UnwrapRef - readonly useScriptRybbitAnalytics: UnwrapRef - readonly useScriptSegment: UnwrapRef - readonly useScriptSnapchatPixel: UnwrapRef - readonly useScriptStripe: UnwrapRef - readonly useScriptTriggerConsent: UnwrapRef - readonly useScriptTriggerElement: UnwrapRef - readonly useScriptUmamiAnalytics: UnwrapRef - readonly useScriptVimeoPlayer: UnwrapRef - readonly useScriptXPixel: UnwrapRef - readonly useScriptYouTubePlayer: UnwrapRef - readonly useSeoMeta: UnwrapRef - readonly useServerHead: UnwrapRef - readonly useServerHeadSafe: UnwrapRef - readonly useServerSeoMeta: UnwrapRef - readonly useShadowRoot: UnwrapRef - readonly useSlots: UnwrapRef - readonly useState: UnwrapRef - readonly useTemplateRef: UnwrapRef - readonly useTransitionState: UnwrapRef - readonly watch: UnwrapRef - readonly watchEffect: UnwrapRef - readonly watchPostEffect: UnwrapRef - readonly watchSyncEffect: UnwrapRef - readonly withCtx: UnwrapRef - readonly withDirectives: UnwrapRef - readonly withKeys: UnwrapRef - readonly withMemo: UnwrapRef - readonly withModifiers: UnwrapRef - readonly withScopeId: UnwrapRef - } -} -``` - ---- - -## .nuxt/types/layouts.d.ts - -```ts -import type { ComputedRef, MaybeRef } from 'vue' -declare module 'nuxt/app' { - interface NuxtLayouts { -} - export type LayoutKey = keyof NuxtLayouts extends never ? string : keyof NuxtLayouts - interface PageMeta { - layout?: MaybeRef | ComputedRef - } -} -``` - ---- - -## .nuxt/types/middleware.d.ts - -```ts -import type { NavigationGuard } from 'vue-router' -export type MiddlewareKey = never -declare module 'nuxt/app' { - interface PageMeta { - middleware?: MiddlewareKey | NavigationGuard | Array - } -} -``` - ---- - -## .nuxt/types/nitro-config.d.ts - -```ts -// Generated by nitro - -// App Config -import type { Defu } from 'defu' - - - -type UserAppConfig = Defu<{}, []> - -declare module "nitropack/types" { - interface AppConfig extends UserAppConfig {} - -} -export {} -``` - ---- - -## .nuxt/types/nitro-imports.d.ts - -```ts -declare global { - const H3Error: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').H3Error - const H3Event: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').H3Event - const __buildAssetsURL: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@3.21.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@_b2c4b6af65a3b405b9ab78da468773bd/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths').buildAssetsURL - const __publicAssetsURL: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@3.21.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@_b2c4b6af65a3b405b9ab78da468773bd/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths').publicAssetsURL - const appendCorsHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendCorsHeaders - const appendCorsPreflightHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendCorsPreflightHeaders - const appendHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendHeader - const appendHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendHeaders - const appendResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendResponseHeader - const appendResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendResponseHeaders - const assertMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').assertMethod - const cachedEventHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').cachedEventHandler - const cachedFunction: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').cachedFunction - const callNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').callNodeListener - const clearResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').clearResponseHeaders - const clearSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').clearSession - const createApp: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createApp - const createAppEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createAppEventHandler - const createError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createError - const createEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createEvent - const createEventStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createEventStream - const createRouter: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createRouter - const defaultContentType: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defaultContentType - const defineAppConfig: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@3.21.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@_b2c4b6af65a3b405b9ab78da468773bd/node_modules/@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig - const defineCachedEventHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').defineCachedEventHandler - const defineCachedFunction: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').defineCachedFunction - const defineEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineEventHandler - const defineLazyEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineLazyEventHandler - const defineNitroErrorHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/error/utils').defineNitroErrorHandler - const defineNitroPlugin: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/plugin').defineNitroPlugin - const defineNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineNodeListener - const defineNodeMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineNodeMiddleware - const defineRenderHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/renderer').defineRenderHandler - const defineRequestMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineRequestMiddleware - const defineResponseMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineResponseMiddleware - const defineRouteMeta: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/meta').defineRouteMeta - const defineTask: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/task').defineTask - const defineWebSocket: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineWebSocket - const defineWebSocketHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineWebSocketHandler - const deleteCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').deleteCookie - const dynamicEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').dynamicEventHandler - const eventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').eventHandler - const fetchWithEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fetchWithEvent - const fromNodeMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromNodeMiddleware - const fromPlainHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromPlainHandler - const fromWebHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromWebHandler - const getCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getCookie - const getHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getHeader - const getHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getHeaders - const getMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getMethod - const getOrCreateUser: typeof import('../../server/utils/users').getOrCreateUser - const getProxyRequestHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getProxyRequestHeaders - const getQuery: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getQuery - const getRequestFingerprint: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestFingerprint - const getRequestHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHeader - const getRequestHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHeaders - const getRequestHost: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHost - const getRequestIP: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestIP - const getRequestPath: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestPath - const getRequestProtocol: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestProtocol - const getRequestURL: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestURL - const getRequestWebStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestWebStream - const getResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseHeader - const getResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseHeaders - const getResponseStatus: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseStatus - const getResponseStatusText: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseStatusText - const getRouteRules: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/route-rules').getRouteRules - const getRouterParam: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRouterParam - const getRouterParams: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRouterParams - const getSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getSession - const getValidatedQuery: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getValidatedQuery - const getValidatedRouterParams: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getValidatedRouterParams - const handleCacheHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').handleCacheHeaders - const handleCors: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').handleCors - const incrementBurritoConsiderations: typeof import('../../server/utils/users').incrementBurritoConsiderations - const isCorsOriginAllowed: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isCorsOriginAllowed - const isError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isError - const isEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isEvent - const isEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isEventHandler - const isMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isMethod - const isPreflightRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isPreflightRequest - const isStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isStream - const isWebResponse: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isWebResponse - const lazyEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').lazyEventHandler - const nitroPlugin: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/plugin').nitroPlugin - const parseCookies: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').parseCookies - const promisifyNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').promisifyNodeListener - const proxyRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').proxyRequest - const readBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readBody - const readFormData: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readFormData - const readMultipartFormData: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readMultipartFormData - const readRawBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readRawBody - const readValidatedBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readValidatedBody - const removeResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').removeResponseHeader - const runTask: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/task').runTask - const sanitizeStatusCode: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sanitizeStatusCode - const sanitizeStatusMessage: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sanitizeStatusMessage - const sealSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sealSession - const send: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').send - const sendError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendError - const sendIterable: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendIterable - const sendNoContent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendNoContent - const sendProxy: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendProxy - const sendRedirect: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendRedirect - const sendStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendStream - const sendWebResponse: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendWebResponse - const serveStatic: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').serveStatic - const setCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setCookie - const setHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setHeader - const setHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setHeaders - const setResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseHeader - const setResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseHeaders - const setResponseStatus: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseStatus - const splitCookiesString: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').splitCookiesString - const toEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toEventHandler - const toNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toNodeListener - const toPlainHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toPlainHandler - const toWebHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toWebHandler - const toWebRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toWebRequest - const unsealSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').unsealSession - const updateSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').updateSession - const useAppConfig: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/config').useAppConfig - const useBase: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').useBase - const useEvent: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/context').useEvent - const useNitroApp: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/app').useNitroApp - const useRuntimeConfig: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/config').useRuntimeConfig - const useSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').useSession - const useStorage: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/storage').useStorage - const users: typeof import('../../server/utils/users').users - const writeEarlyHints: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').writeEarlyHints -} -// for type re-export -declare global { - // @ts-ignore - export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../node_modules/.pnpm/h3@1.15.5/node_modules/h3' - import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3') -} -export { H3Event, H3Error, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearResponseHeaders, clearSession, createApp, createAppEventHandler, createError, createEvent, createEventStream, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, defineRequestMiddleware, defineResponseMiddleware, defineWebSocket, defineWebSocketHandler, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, fromPlainHandler, fromWebHandler, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, getValidatedRouterParams, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, isWebResponse, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readFormData, readMultipartFormData, readRawBody, readValidatedBody, removeResponseHeader, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, toPlainHandler, toWebHandler, toWebRequest, unsealSession, updateSession, useBase, useSession, writeEarlyHints } from 'h3'; -export { useNitroApp } from 'nitropack/runtime/internal/app'; -export { useRuntimeConfig, useAppConfig } from 'nitropack/runtime/internal/config'; -export { defineNitroPlugin, nitroPlugin } from 'nitropack/runtime/internal/plugin'; -export { defineCachedFunction, defineCachedEventHandler, cachedFunction, cachedEventHandler } from 'nitropack/runtime/internal/cache'; -export { useStorage } from 'nitropack/runtime/internal/storage'; -export { defineRenderHandler } from 'nitropack/runtime/internal/renderer'; -export { defineRouteMeta } from 'nitropack/runtime/internal/meta'; -export { getRouteRules } from 'nitropack/runtime/internal/route-rules'; -export { useEvent } from 'nitropack/runtime/internal/context'; -export { defineTask, runTask } from 'nitropack/runtime/internal/task'; -export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils'; -export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/Users/kaia/code/amp/context-hub/basics/nuxt-3.6/node_modules/.pnpm/@nuxt+nitro-server@3.21.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@_b2c4b6af65a3b405b9ab78da468773bd/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths'; -export { defineAppConfig } from '/Users/kaia/code/amp/context-hub/basics/nuxt-3.6/node_modules/.pnpm/@nuxt+nitro-server@3.21.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@_b2c4b6af65a3b405b9ab78da468773bd/node_modules/@nuxt/nitro-server/dist/runtime/utils/config'; -export { users, getOrCreateUser, incrementBurritoConsiderations } from '/Users/kaia/code/amp/context-hub/basics/nuxt-3.6/server/utils/users'; -``` - ---- - -## .nuxt/types/nitro-layouts.d.ts - -```ts -export type LayoutKey = string -declare module 'nitropack' { - interface NitroRouteConfig { - appLayout?: LayoutKey | false - } - interface NitroRouteRules { - appLayout?: LayoutKey | false - } -} -declare module 'nitropack/types' { - interface NitroRouteConfig { - appLayout?: LayoutKey | false - } - interface NitroRouteRules { - appLayout?: LayoutKey | false - } -} -``` - ---- - -## .nuxt/types/nitro-middleware.d.ts - -```ts -export type MiddlewareKey = never -declare module 'nitropack' { - interface NitroRouteConfig { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } - interface NitroRouteRules { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } -} -declare module 'nitropack/types' { - interface NitroRouteConfig { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } - interface NitroRouteRules { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } -} -``` - ---- - -## .nuxt/types/nitro-nuxt.d.ts - -```ts - -/// -/// -/// -/// -/// -/// - -import type { RuntimeConfig } from 'nuxt/schema' -import type { H3Event } from 'h3' -import type { LogObject } from 'consola' -import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app' - -declare module 'nitropack' { - interface NitroRuntimeConfigApp { - buildAssetsDir: string - cdnURL: string - } - interface NitroRuntimeConfig extends RuntimeConfig {} - interface NitroRouteConfig { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - } - interface NitroRouteRules { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - appMiddleware?: Record - appLayout?: string | false - } - interface NitroRuntimeHooks { - 'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise - 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise - 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise - } -} - -``` - ---- - -## .nuxt/types/nitro-routes.d.ts - -```ts -// Generated by nitro -import type { Serialize, Simplify } from "nitropack/types"; -declare module "nitropack/types" { - type Awaited = T extends PromiseLike ? Awaited : T - interface InternalApi { - '/api/auth/login': { - 'post': Simplify>>> - } - '/api/burrito/consider': { - 'post': Simplify>>> - } - '/__nuxt_error': { - 'default': Simplify>>> - } - '/__nuxt_island/**': { - 'default': Simplify>>> - } - } -} -export {} -``` - ---- - -## .nuxt/types/nitro.d.ts - -```ts -/// -/// -/// -``` - ---- - -## .nuxt/types/plugins.d.ts - -```ts -// Generated by Nuxt' -import type { Plugin } from '#app' - -type Decorate> = { [K in keyof T as K extends string ? `$${K}` : never]: T[K] } - -type InjectionType = A extends {default: Plugin} ? Decorate : unknown - -type NuxtAppInjections = - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType - -declare module '#app' { - interface NuxtApp extends NuxtAppInjections { } - - interface NuxtAppLiterals { - pluginName: 'nuxt:revive-payload:client' | 'nuxt:head' | 'nuxt:router' | 'nuxt:payload' | 'nuxt:revive-payload:server' | 'nuxt:chunk-reload' | 'nuxt:global-components' | 'nuxt:prefetch' - } -} - -declare module 'vue' { - interface ComponentCustomProperties extends NuxtAppInjections { } -} - -export { } - -``` - ---- - -## .nuxt/types/schema.d.ts - -```ts -import { RuntimeConfig as UserRuntimeConfig, PublicRuntimeConfig as UserPublicRuntimeConfig } from 'nuxt/schema' -import { NuxtModule, ModuleDependencyMeta } from '@nuxt/schema' - interface SharedRuntimeConfig { - app: { - buildId: string, - - baseURL: string, - - buildAssetsDir: string, - - cdnURL: string, - }, - - nitro: { - envPrefix: string, - }, - } - interface SharedPublicRuntimeConfig { - amplitudeApiKey: string, - } -declare module '@nuxt/schema' { - interface ModuleDependencies { - ["@nuxt/devtools"]?: ModuleDependencyMeta ? O | false : Record> | false - ["@nuxt/telemetry"]?: ModuleDependencyMeta ? O | false : Record> | false - } - interface NuxtOptions { - /** - * Configuration for `@nuxt/devtools` - */ - ["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule ? O | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - */ - ["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule ? O | false : Record | false - } - interface NuxtConfig { - /** - * Configuration for `@nuxt/devtools` - */ - ["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule ? Partial | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - */ - ["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule ? Partial | false : Record | false - modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ["@nuxt/devtools", Exclude] | ["@nuxt/telemetry", Exclude])[], - } - interface RuntimeConfig extends UserRuntimeConfig {} - interface PublicRuntimeConfig extends UserPublicRuntimeConfig {} -} -declare module 'nuxt/schema' { - interface ModuleDependencies { - ["@nuxt/devtools"]?: ModuleDependencyMeta ? O | false : Record> | false - ["@nuxt/telemetry"]?: ModuleDependencyMeta ? O | false : Record> | false - } - interface NuxtOptions { - /** - * Configuration for `@nuxt/devtools` - * @see https://www.npmjs.com/package/@nuxt/devtools - */ - ["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule ? O | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - * @see https://www.npmjs.com/package/@nuxt/telemetry - */ - ["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule ? O | false : Record | false - } - interface NuxtConfig { - /** - * Configuration for `@nuxt/devtools` - * @see https://www.npmjs.com/package/@nuxt/devtools - */ - ["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule ? Partial | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - * @see https://www.npmjs.com/package/@nuxt/telemetry - */ - ["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule ? Partial | false : Record | false - modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ["@nuxt/devtools", Exclude] | ["@nuxt/telemetry", Exclude])[], - } - interface RuntimeConfig extends SharedRuntimeConfig {} - interface PublicRuntimeConfig extends SharedPublicRuntimeConfig {} -} -declare module 'vue' { - interface ComponentCustomProperties { - $config: UserRuntimeConfig - } - } -``` - ---- - -## .output/public/_nuxt/B77IIWzB.js - -```js -import{f as x,g as c,h as U,o as a,c as l,i as o,F as f,a as s,t as v,j as y,v as g,k as w,l as V,m as B,n as C,r as d,I}from"./BMw6m4EL.js";const N={class:"container"},S={class:"form-group"},j={class:"form-group"},A={key:0,class:"error"},P=x({__name:"index",setup(E){const p=c(),m=C(()=>p.user.value),n=d(""),t=d(""),r=d(""),{$amplitude:u}=U(),b=async()=>{if(r.value="",await p.login(n.value,t.value)){u.setUserId(n.value);const e=new I;e.set("username",n.value),u.identify(e),u.track("user_logged_in",{username:n.value}),n.value="",t.value=""}else r.value="Please provide both username and password"};return(k,e)=>(a(),l("div",N,[o(m)?(a(),l(f,{key:0},[s("h1",null,"Welcome back, "+v(o(m).username)+"!",1),e[2]||(e[2]=s("p",null,"You are now logged in. Feel free to explore:",-1)),e[3]||(e[3]=s("ul",null,[s("li",null,"Consider the potential of burritos"),s("li",null,"View your profile and statistics")],-1))],64)):(a(),l(f,{key:1},[e[7]||(e[7]=s("h1",null,"Welcome to Burrito Consideration App",-1)),e[8]||(e[8]=s("p",null,"Please sign in to begin your burrito journey",-1)),s("form",{onSubmit:B(b,["prevent"]),class:"form"},[s("div",S,[e[4]||(e[4]=s("label",{for:"username"},"Username:",-1)),y(s("input",{type:"text",id:"username","onUpdate:modelValue":e[0]||(e[0]=i=>w(n)?n.value=i:null),placeholder:"Enter any username"},null,512),[[g,o(n)]])]),s("div",j,[e[5]||(e[5]=s("label",{for:"password"},"Password:",-1)),y(s("input",{type:"password",id:"password","onUpdate:modelValue":e[1]||(e[1]=i=>w(t)?t.value=i:null),placeholder:"Enter any password"},null,512),[[g,o(t)]])]),o(r)?(a(),l("p",A,v(o(r)),1)):V("",!0),e[6]||(e[6]=s("button",{type:"submit",class:"btn-primary"},"Sign In",-1))],32),e[9]||(e[9]=s("p",{class:"note"}," Note: This is a demo app. Use any username and password to sign in. ",-1))],64))]))}});export{P as default}; - -``` - ---- - -## .output/public/_nuxt/BMw6m4EL.js - -```js -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./MzFTpsyS.js","./error-404.Cz778Gtt.css","./z593H3Qg.js","./error-500.D1XFhX37.css"])))=>i.map(i=>d[i]); -function sy(e,t){for(var r=0;rn[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const s of o.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function r(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function n(i){if(i.ep)return;i.ep=!0;const o=r(i);fetch(i.href,o)}})();function il(e){const t=Object.create(null);for(const r of e.split(","))t[r]=1;return r=>r in t}const xe={},Rn=[],Jt=()=>{},Rh=()=>!1,Ji=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),ol=e=>e.startsWith("onUpdate:"),Je=Object.assign,sl=(e,t)=>{const r=e.indexOf(t);r>-1&&e.splice(r,1)},ay=Object.prototype.hasOwnProperty,Ie=(e,t)=>ay.call(e,t),ve=Array.isArray,On=e=>Qi(e)==="[object Map]",Oh=e=>Qi(e)==="[object Set]",uy=e=>Qi(e)==="[object RegExp]",he=e=>typeof e=="function",Be=e=>typeof e=="string",Hr=e=>typeof e=="symbol",Ue=e=>e!==null&&typeof e=="object",al=e=>(Ue(e)||he(e))&&he(e.then)&&he(e.catch),xh=Object.prototype.toString,Qi=e=>xh.call(e),ly=e=>Qi(e).slice(8,-1),Nh=e=>Qi(e)==="[object Object]",Ts=e=>Be(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,tn=il(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),As=e=>{const t=Object.create(null);return(r=>t[r]||(t[r]=e(r)))},cy=/-\w/g,Mt=As(e=>e.replace(cy,t=>t.slice(1).toUpperCase())),fy=/\B([A-Z])/g,dn=As(e=>e.replace(fy,"-$1").toLowerCase()),Cs=As(e=>e.charAt(0).toUpperCase()+e.slice(1)),Ys=As(e=>e?`on${Cs(e)}`:""),Dr=(e,t)=>!Object.is(e,t),xn=(e,...t)=>{for(let r=0;r{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:r})},ul=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Mh=e=>{const t=Be(e)?Number(e):NaN;return isNaN(t)?e:t};let ac;const Is=()=>ac||(ac=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ks(e){if(ve(e)){const t={};for(let r=0;r{if(r){const n=r.split(hy);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Ps(e){let t="";if(Be(e))t=e;else if(ve(e))for(let r=0;r!!(e&&e.__v_isRef===!0),Fh=e=>Be(e)?e:e==null?"":ve(e)||Ue(e)&&(e.toString===xh||!he(e.toString))?Uh(e)?Fh(e.value):JSON.stringify(e,Hh,2):String(e),Hh=(e,t)=>Uh(t)?Hh(e,t.value):On(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((r,[n,i],o)=>(r[Js(n,o)+" =>"]=i,r),{})}:Oh(t)?{[`Set(${t.size})`]:[...t.values()].map(r=>Js(r))}:Hr(t)?Js(t):Ue(t)&&!ve(t)&&!Nh(t)?String(t):t,Js=(e,t="")=>{var r;return Hr(e)?`Symbol(${(r=e.description)!=null?r:t})`:e};let st;class Bh{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=st,!t&&st&&(this.index=(st.scopes||(st.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,r;if(this.scopes)for(t=0,r=this.scopes.length;t0&&--this._on===0&&(st=this.prevScope,this.prevScope=void 0)}stop(t){if(this._active){this._active=!1;let r,n;for(r=0,n=this.effects.length;r0)return;if(Ti){let t=Ti;for(Ti=void 0;t;){const r=t.next;t.next=void 0,t.flags&=-9,t=r}}let e;for(;wi;){let t=wi;for(wi=void 0;t;){const r=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=r}}if(e)throw e}function $h(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function Wh(e){let t,r=e.depsTail,n=r;for(;n;){const i=n.prevDep;n.version===-1?(n===r&&(r=i),dl(n),_y(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=i}e.deps=t,e.depsTail=r}function Ka(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Gh(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Gh(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Bi)||(e.globalVersion=Bi,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Ka(e))))return;e.flags|=2;const t=e.dep,r=Le,n=jt;Le=e,jt=!0;try{$h(e);const i=e.fn(e._value);(t.version===0||Dr(i,e._value))&&(e.flags|=128,e._value=i,t.version++)}catch(i){throw t.version++,i}finally{Le=r,jt=n,Wh(e),e.flags&=-3}}function dl(e,t=!1){const{dep:r,prevSub:n,nextSub:i}=e;if(n&&(n.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=n,e.nextSub=void 0),r.subs===e&&(r.subs=n,!n&&r.computed)){r.computed.flags&=-5;for(let o=r.computed.deps;o;o=o.nextDep)dl(o,!0)}!t&&!--r.sc&&r.map&&r.map.delete(r.key)}function _y(e){const{prevDep:t,nextDep:r}=e;t&&(t.nextDep=r,e.prevDep=void 0),r&&(r.prevDep=t,e.nextDep=void 0)}let jt=!0;const Kh=[];function pr(){Kh.push(jt),jt=!1}function gr(){const e=Kh.pop();jt=e===void 0?!0:e}function uc(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const r=Le;Le=void 0;try{t()}finally{Le=r}}}let Bi=0;class Sy{constructor(t,r){this.sub=t,this.dep=r,this.version=r.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class hl{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!Le||!jt||Le===this.computed)return;let r=this.activeLink;if(r===void 0||r.sub!==Le)r=this.activeLink=new Sy(Le,this),Le.deps?(r.prevDep=Le.depsTail,Le.depsTail.nextDep=r,Le.depsTail=r):Le.deps=Le.depsTail=r,zh(r);else if(r.version===-1&&(r.version=this.version,r.nextDep)){const n=r.nextDep;n.prevDep=r.prevDep,r.prevDep&&(r.prevDep.nextDep=n),r.prevDep=Le.depsTail,r.nextDep=void 0,Le.depsTail.nextDep=r,Le.depsTail=r,Le.deps===r&&(Le.deps=n)}return r}trigger(t){this.version++,Bi++,this.notify(t)}notify(t){cl();try{for(let r=this.subs;r;r=r.prevSub)r.sub.notify()&&r.sub.dep.notify()}finally{fl()}}}function zh(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)zh(n)}const r=e.dep.subs;r!==e&&(e.prevSub=r,r&&(r.nextSub=e)),e.dep.subs=e}}const ts=new WeakMap,rn=Symbol(""),za=Symbol(""),ji=Symbol("");function ut(e,t,r){if(jt&&Le){let n=ts.get(e);n||ts.set(e,n=new Map);let i=n.get(r);i||(n.set(r,i=new hl),i.map=n,i.key=r),i.track()}}function fr(e,t,r,n,i,o){const s=ts.get(e);if(!s){Bi++;return}const a=u=>{u&&u.trigger()};if(cl(),t==="clear")s.forEach(a);else{const u=ve(e),c=u&&Ts(r);if(u&&r==="length"){const l=Number(n);s.forEach((f,d)=>{(d==="length"||d===ji||!Hr(d)&&d>=l)&&a(f)})}else switch((r!==void 0||s.has(void 0))&&a(s.get(r)),c&&a(s.get(ji)),t){case"add":u?c&&a(s.get("length")):(a(s.get(rn)),On(e)&&a(s.get(za)));break;case"delete":u||(a(s.get(rn)),On(e)&&a(s.get(za)));break;case"set":On(e)&&a(s.get(rn));break}}fl()}function wy(e,t){const r=ts.get(e);return r&&r.get(t)}function gn(e){const t=Ae(e);return t===e?t:(ut(t,"iterate",ji),Ot(e)?t:t.map(yr))}function vl(e){return ut(e=Ae(e),"iterate",ji),e}function Tr(e,t){return mr(e)?qi(nn(e)?yr(t):t):yr(t)}const Ty={__proto__:null,[Symbol.iterator](){return Zs(this,Symbol.iterator,e=>Tr(this,e))},concat(...e){return gn(this).concat(...e.map(t=>ve(t)?gn(t):t))},entries(){return Zs(this,"entries",e=>(e[1]=Tr(this,e[1]),e))},every(e,t){return nr(this,"every",e,t,void 0,arguments)},filter(e,t){return nr(this,"filter",e,t,r=>r.map(n=>Tr(this,n)),arguments)},find(e,t){return nr(this,"find",e,t,r=>Tr(this,r),arguments)},findIndex(e,t){return nr(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return nr(this,"findLast",e,t,r=>Tr(this,r),arguments)},findLastIndex(e,t){return nr(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return nr(this,"forEach",e,t,void 0,arguments)},includes(...e){return ea(this,"includes",e)},indexOf(...e){return ea(this,"indexOf",e)},join(e){return gn(this).join(e)},lastIndexOf(...e){return ea(this,"lastIndexOf",e)},map(e,t){return nr(this,"map",e,t,void 0,arguments)},pop(){return oi(this,"pop")},push(...e){return oi(this,"push",e)},reduce(e,...t){return lc(this,"reduce",e,t)},reduceRight(e,...t){return lc(this,"reduceRight",e,t)},shift(){return oi(this,"shift")},some(e,t){return nr(this,"some",e,t,void 0,arguments)},splice(...e){return oi(this,"splice",e)},toReversed(){return gn(this).toReversed()},toSorted(e){return gn(this).toSorted(e)},toSpliced(...e){return gn(this).toSpliced(...e)},unshift(...e){return oi(this,"unshift",e)},values(){return Zs(this,"values",e=>Tr(this,e))}};function Zs(e,t,r){const n=vl(e),i=n[t]();return n!==e&&!Ot(e)&&(i._next=i.next,i.next=()=>{const o=i._next();return o.done||(o.value=r(o.value)),o}),i}const Ay=Array.prototype;function nr(e,t,r,n,i,o){const s=vl(e),a=s!==e&&!Ot(e),u=s[t];if(u!==Ay[t]){const f=u.apply(e,o);return a?yr(f):f}let c=r;s!==e&&(a?c=function(f,d){return r.call(this,Tr(e,f),d,e)}:r.length>2&&(c=function(f,d){return r.call(this,f,d,e)}));const l=u.call(s,c,n);return a&&i?i(l):l}function lc(e,t,r,n){const i=vl(e);let o=r;return i!==e&&(Ot(e)?r.length>3&&(o=function(s,a,u){return r.call(this,s,a,u,e)}):o=function(s,a,u){return r.call(this,s,Tr(e,a),u,e)}),i[t](o,...n)}function ea(e,t,r){const n=Ae(e);ut(n,"iterate",ji);const i=n[t](...r);return(i===-1||i===!1)&&Rs(r[0])?(r[0]=Ae(r[0]),n[t](...r)):i}function oi(e,t,r=[]){pr(),cl();const n=Ae(e)[t].apply(e,r);return fl(),gr(),n}const Cy=il("__proto__,__v_isRef,__isVue"),Xh=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Hr));function Iy(e){Hr(e)||(e=String(e));const t=Ae(this);return ut(t,"has",e),t.hasOwnProperty(e)}class Yh{constructor(t=!1,r=!1){this._isReadonly=t,this._isShallow=r}get(t,r,n){if(r==="__v_skip")return t.__v_skip;const i=this._isReadonly,o=this._isShallow;if(r==="__v_isReactive")return!i;if(r==="__v_isReadonly")return i;if(r==="__v_isShallow")return o;if(r==="__v_raw")return n===(i?o?Uy:ev:o?Zh:Qh).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const s=ve(t);if(!i){let u;if(s&&(u=Ty[r]))return u;if(r==="hasOwnProperty")return Iy}const a=Reflect.get(t,r,Ve(t)?t:n);if((Hr(r)?Xh.has(r):Cy(r))||(i||ut(t,"get",r),o))return a;if(Ve(a)){const u=s&&Ts(r)?a:a.value;return i&&Ue(u)?Ya(u):u}return Ue(a)?i?Ya(a):Br(a):a}}class Jh extends Yh{constructor(t=!1){super(!1,t)}set(t,r,n,i){let o=t[r];const s=ve(t)&&Ts(r);if(!this._isShallow){const c=mr(o);if(!Ot(n)&&!mr(n)&&(o=Ae(o),n=Ae(n)),!s&&Ve(o)&&!Ve(n))return c||(o.value=n),!0}const a=s?Number(r)e,ao=e=>Reflect.getPrototypeOf(e);function xy(e,t,r){return function(...n){const i=this.__v_raw,o=Ae(i),s=On(o),a=e==="entries"||e===Symbol.iterator&&s,u=e==="keys"&&s,c=i[e](...n),l=r?Xa:t?qi:yr;return!t&&ut(o,"iterate",u?za:rn),Je(Object.create(c),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:a?[l(f[0]),l(f[1])]:l(f),done:d}}})}}function uo(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Ny(e,t){const r={get(i){const o=this.__v_raw,s=Ae(o),a=Ae(i);e||(Dr(i,a)&&ut(s,"get",i),ut(s,"get",a));const{has:u}=ao(s),c=t?Xa:e?qi:yr;if(u.call(s,i))return c(o.get(i));if(u.call(s,a))return c(o.get(a));o!==s&&o.get(i)},get size(){const i=this.__v_raw;return!e&&ut(Ae(i),"iterate",rn),i.size},has(i){const o=this.__v_raw,s=Ae(o),a=Ae(i);return e||(Dr(i,a)&&ut(s,"has",i),ut(s,"has",a)),i===a?o.has(i):o.has(i)||o.has(a)},forEach(i,o){const s=this,a=s.__v_raw,u=Ae(a),c=t?Xa:e?qi:yr;return!e&&ut(u,"iterate",rn),a.forEach((l,f)=>i.call(o,c(l),c(f),s))}};return Je(r,e?{add:uo("add"),set:uo("set"),delete:uo("delete"),clear:uo("clear")}:{add(i){!t&&!Ot(i)&&!mr(i)&&(i=Ae(i));const o=Ae(this);return ao(o).has.call(o,i)||(o.add(i),fr(o,"add",i,i)),this},set(i,o){!t&&!Ot(o)&&!mr(o)&&(o=Ae(o));const s=Ae(this),{has:a,get:u}=ao(s);let c=a.call(s,i);c||(i=Ae(i),c=a.call(s,i));const l=u.call(s,i);return s.set(i,o),c?Dr(o,l)&&fr(s,"set",i,o):fr(s,"add",i,o),this},delete(i){const o=Ae(this),{has:s,get:a}=ao(o);let u=s.call(o,i);u||(i=Ae(i),u=s.call(o,i)),a&&a.call(o,i);const c=o.delete(i);return u&&fr(o,"delete",i,void 0),c},clear(){const i=Ae(this),o=i.size!==0,s=i.clear();return o&&fr(i,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(i=>{r[i]=xy(i,e,t)}),r}function pl(e,t){const r=Ny(e,t);return(n,i,o)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?n:Reflect.get(Ie(r,i)&&i in n?r:n,i,o)}const Ly={get:pl(!1,!1)},My={get:pl(!1,!0)},Dy={get:pl(!0,!1)};const Qh=new WeakMap,Zh=new WeakMap,ev=new WeakMap,Uy=new WeakMap;function Fy(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function Hy(e){return e.__v_skip||!Object.isExtensible(e)?0:Fy(ly(e))}function Br(e){return mr(e)?e:gl(e,!1,Py,Ly,Qh)}function hr(e){return gl(e,!1,Oy,My,Zh)}function Ya(e){return gl(e,!0,Ry,Dy,ev)}function gl(e,t,r,n,i){if(!Ue(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=Hy(e);if(o===0)return e;const s=i.get(e);if(s)return s;const a=new Proxy(e,o===2?n:r);return i.set(e,a),a}function nn(e){return mr(e)?nn(e.__v_raw):!!(e&&e.__v_isReactive)}function mr(e){return!!(e&&e.__v_isReadonly)}function Ot(e){return!!(e&&e.__v_isShallow)}function Rs(e){return e?!!e.__v_raw:!1}function Ae(e){const t=e&&e.__v_raw;return t?Ae(t):e}function By(e){return!Ie(e,"__v_skip")&&Object.isExtensible(e)&&Lh(e,"__v_skip",!0),e}const yr=e=>Ue(e)?Br(e):e,qi=e=>Ue(e)?Ya(e):e;function Ve(e){return e?e.__v_isRef===!0:!1}function Qt(e){return tv(e,!1)}function br(e){return tv(e,!0)}function tv(e,t){return Ve(e)?e:new jy(e,t)}class jy{constructor(t,r){this.dep=new hl,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=r?t:Ae(t),this._value=r?t:yr(t),this.__v_isShallow=r}get value(){return this.dep.track(),this._value}set value(t){const r=this._rawValue,n=this.__v_isShallow||Ot(t)||mr(t);t=n?t:Ae(t),Dr(t,r)&&(this._rawValue=t,this._value=n?t:yr(t),this.dep.trigger())}}function ke(e){return Ve(e)?e.value:e}function qy(e){return he(e)?e():ke(e)}const Vy={get:(e,t,r)=>t==="__v_raw"?e:ke(Reflect.get(e,t,r)),set:(e,t,r,n)=>{const i=e[t];return Ve(i)&&!Ve(r)?(i.value=r,!0):Reflect.set(e,t,r,n)}};function rv(e){return nn(e)?e:new Proxy(e,Vy)}class $y{constructor(t,r,n){this._object=t,this._key=r,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0,this._raw=Ae(t);let i=!0,o=t;if(!ve(t)||!Ts(String(r)))do i=!Rs(o)||Ot(o);while(i&&(o=o.__v_raw));this._shallow=i}get value(){let t=this._object[this._key];return this._shallow&&(t=ke(t)),this._value=t===void 0?this._defaultValue:t}set value(t){if(this._shallow&&Ve(this._raw[this._key])){const r=this._object[this._key];if(Ve(r)){r.value=t;return}}this._object[this._key]=t}get dep(){return wy(this._raw,this._key)}}class Wy{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function nv(e,t,r){return Ve(e)?e:he(e)?new Wy(e):Ue(e)&&arguments.length>1?Gy(e,t,r):Qt(e)}function Gy(e,t,r){return new $y(e,t,r)}class Ky{constructor(t,r,n){this.fn=t,this.setter=r,this._value=void 0,this.dep=new hl(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Bi-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!r,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&Le!==this)return Vh(this,!0),!0}get value(){const t=this.dep.track();return Gh(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function zy(e,t,r=!1){let n,i;return he(e)?n=e:(n=e.get,i=e.set),new Ky(n,i,r)}const lo={},rs=new WeakMap;let Xr;function Xy(e,t=!1,r=Xr){if(r){let n=rs.get(r);n||rs.set(r,n=[]),n.push(e)}}function Yy(e,t,r=xe){const{immediate:n,deep:i,once:o,scheduler:s,augmentJob:a,call:u}=r,c=y=>i?y:Ot(y)||i===!1||i===0?dr(y,1):dr(y);let l,f,d,h,v=!1,p=!1;if(Ve(e)?(f=()=>e.value,v=Ot(e)):nn(e)?(f=()=>c(e),v=!0):ve(e)?(p=!0,v=e.some(y=>nn(y)||Ot(y)),f=()=>e.map(y=>{if(Ve(y))return y.value;if(nn(y))return c(y);if(he(y))return u?u(y,2):y()})):he(e)?t?f=u?()=>u(e,2):e:f=()=>{if(d){pr();try{d()}finally{gr()}}const y=Xr;Xr=l;try{return u?u(e,3,[h]):e(h)}finally{Xr=y}}:f=Jt,t&&i){const y=f,_=i===!0?1/0:i;f=()=>dr(y(),_)}const b=ll(),E=()=>{l.stop(),b&&b.active&&sl(b.effects,l)};if(o&&t){const y=t;t=(..._)=>{y(..._),E()}}let m=p?new Array(e.length).fill(lo):lo;const g=y=>{if(!(!(l.flags&1)||!l.dirty&&!y))if(t){const _=l.run();if(i||v||(p?_.some((S,A)=>Dr(S,m[A])):Dr(_,m))){d&&d();const S=Xr;Xr=l;try{const A=[_,m===lo?void 0:p&&m[0]===lo?[]:m,h];m=_,u?u(t,3,A):t(...A)}finally{Xr=S}}}else l.run()};return a&&a(g),l=new jh(f),l.scheduler=s?()=>s(g,!1):g,h=y=>Xy(y,!1,l),d=l.onStop=()=>{const y=rs.get(l);if(y){if(u)u(y,4);else for(const _ of y)_();rs.delete(l)}},t?n?g(!0):m=l.run():s?s(g.bind(null,!0),!0):l.run(),E.pause=l.pause.bind(l),E.resume=l.resume.bind(l),E.stop=E,E}function dr(e,t=1/0,r){if(t<=0||!Ue(e)||e.__v_skip||(r=r||new Map,(r.get(e)||0)>=t))return e;if(r.set(e,t),t--,Ve(e))dr(e.value,t,r);else if(ve(e))for(let n=0;n{dr(n,t,r)});else if(Nh(e)){for(const n in e)dr(e[n],t,r);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&dr(e[n],t,r)}return e}function Zi(e,t,r,n){try{return n?e(...n):e()}catch(i){Zn(i,t,r)}}function qt(e,t,r,n){if(he(e)){const i=Zi(e,t,r,n);return i&&al(i)&&i.catch(o=>{Zn(o,t,r)}),i}if(ve(e)){const i=[];for(let o=0;o>>1,i=pt[n],o=$i(i);o=$i(r)?pt.push(e):pt.splice(Qy(t),0,e),e.flags|=1,ov()}}function ov(){ns||(ns=iv.then(sv))}function Ja(e){ve(e)?Nn.push(...e):Ar&&e.id===-1?Ar.splice(_n+1,0,e):e.flags&1||(Nn.push(e),e.flags|=1),ov()}function cc(e,t,r=Gt+1){for(;r$i(r)-$i(n));if(Nn.length=0,Ar){Ar.push(...t);return}for(Ar=t,_n=0;_ne.id==null?e.flags&2?-1:1/0:e.id;function sv(e){try{for(Gt=0;Gt{n._d&&cs(-1);const o=os(t);let s;try{s=e(...i)}finally{os(o),n._d&&cs(1)}return s};return n._n=!0,n._c=!0,n._d=!0,n}function mN(e,t){if(Ct===null)return e;const r=Ms(Ct),n=e.dirs||(e.dirs=[]);for(let i=0;i1)return r&&he(t)?t.call(n&&n.proxy):t}}function Os(){return!!(hn()||on)}const Zy=Symbol.for("v-scx"),e0=()=>_t(Zy);function t0(e,t){return yl(e,null,t)}function Mn(e,t,r){return yl(e,t,r)}function yl(e,t,r=xe){const{immediate:n,deep:i,flush:o,once:s}=r,a=Je({},r),u=t&&n||!t&&o!=="post";let c;if(Gn){if(o==="sync"){const h=e0();c=h.__watcherHandles||(h.__watcherHandles=[])}else if(!u){const h=()=>{};return h.stop=Jt,h.resume=Jt,h.pause=Jt,h}}const l=it;a.call=(h,v,p)=>qt(h,l,v,p);let f=!1;o==="post"?a.scheduler=h=>{Qe(h,l&&l.suspense)}:o!=="sync"&&(f=!0,a.scheduler=(h,v)=>{v?h():ml(h)}),a.augmentJob=h=>{t&&(h.flags|=4),f&&(h.flags|=2,l&&(h.id=l.uid,h.i=l))};const d=Yy(e,t,a);return Gn&&(c?c.push(d):u&&d()),d}function r0(e,t,r){const n=this.proxy,i=Be(e)?e.includes(".")?uv(n,e):()=>n[e]:e.bind(n,n);let o;he(t)?o=t:(o=t.handler,r=t);const s=to(this),a=yl(i,o.bind(n),r);return s(),a}function uv(e,t){const r=t.split(".");return()=>{let n=e;for(let i=0;ie.__isTeleport,ur=Symbol("_leaveCb"),co=Symbol("_enterCb");function i0(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Vn(()=>{e.isMounted=!0}),ei(()=>{e.isUnmounting=!0}),e}const It=[Function,Array],cv={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:It,onEnter:It,onAfterEnter:It,onEnterCancelled:It,onBeforeLeave:It,onLeave:It,onAfterLeave:It,onLeaveCancelled:It,onBeforeAppear:It,onAppear:It,onAfterAppear:It,onAppearCancelled:It},fv=e=>{const t=e.subTree;return t.component?fv(t.component):t},o0={name:"BaseTransition",props:cv,setup(e,{slots:t}){const r=hn(),n=i0();return()=>{const i=t.default&&vv(t.default(),!0);if(!i||!i.length)return;const o=dv(i),s=Ae(e),{mode:a}=s;if(n.isLeaving)return ta(o);const u=fc(o);if(!u)return ta(o);let c=Qa(u,s,n,r,f=>c=f);u.type!==Ye&&qn(u,c);let l=r.subTree&&fc(r.subTree);if(l&&l.type!==Ye&&!Bt(l,u)&&fv(r).type!==Ye){let f=Qa(l,s,n,r);if(qn(l,f),a==="out-in"&&u.type!==Ye)return n.isLeaving=!0,f.afterLeave=()=>{n.isLeaving=!1,r.job.flags&8||r.update(),delete f.afterLeave,l=void 0},ta(o);a==="in-out"&&u.type!==Ye?f.delayLeave=(d,h,v)=>{const p=hv(n,l);p[String(l.key)]=l,d[ur]=()=>{h(),d[ur]=void 0,delete c.delayedLeave,l=void 0},c.delayedLeave=()=>{v(),delete c.delayedLeave,l=void 0}}:l=void 0}else l&&(l=void 0);return o}}};function dv(e){let t=e[0];if(e.length>1){for(const r of e)if(r.type!==Ye){t=r;break}}return t}const s0=o0;function hv(e,t){const{leavingVNodes:r}=e;let n=r.get(t.type);return n||(n=Object.create(null),r.set(t.type,n)),n}function Qa(e,t,r,n,i){const{appear:o,mode:s,persisted:a=!1,onBeforeEnter:u,onEnter:c,onAfterEnter:l,onEnterCancelled:f,onBeforeLeave:d,onLeave:h,onAfterLeave:v,onLeaveCancelled:p,onBeforeAppear:b,onAppear:E,onAfterAppear:m,onAppearCancelled:g}=t,y=String(e.key),_=hv(r,e),S=(I,P)=>{I&&qt(I,n,9,P)},A=(I,P)=>{const R=P[1];S(I,P),ve(I)?I.every(k=>k.length<=1)&&R():I.length<=1&&R()},C={mode:s,persisted:a,beforeEnter(I){let P=u;if(!r.isMounted)if(o)P=b||u;else return;I[ur]&&I[ur](!0);const R=_[y];R&&Bt(e,R)&&R.el[ur]&&R.el[ur](),S(P,[I])},enter(I){let P=c,R=l,k=f;if(!r.isMounted)if(o)P=E||c,R=m||l,k=g||f;else return;let N=!1;const $=I[co]=ee=>{N||(N=!0,ee?S(k,[I]):S(R,[I]),C.delayedLeave&&C.delayedLeave(),I[co]=void 0)};P?A(P,[I,$]):$()},leave(I,P){const R=String(e.key);if(I[co]&&I[co](!0),r.isUnmounting)return P();S(d,[I]);let k=!1;const N=I[ur]=$=>{k||(k=!0,P(),$?S(p,[I]):S(v,[I]),I[ur]=void 0,_[R]===e&&delete _[R])};_[R]=e,h?A(h,[I,N]):N()},clone(I){const P=Qa(I,t,r,n,i);return i&&i(P),P}};return C}function ta(e){if(eo(e))return e=xt(e),e.children=null,e}function fc(e){if(!eo(e))return lv(e.type)&&e.children?dv(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:r}=e;if(r){if(t&16)return r[0];if(t&32&&he(r.default))return r.default()}}function qn(e,t){e.shapeFlag&6&&e.component?(e.transition=t,qn(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function vv(e,t=!1,r){let n=[],i=0;for(let o=0;o1)for(let o=0;oDn(v,t&&(ve(t)?t[p]:t),r,n,i));return}if(Ur(n)&&!i){n.shapeFlag&512&&n.type.__asyncResolved&&n.component.subTree.component&&Dn(e,t,r,n.component.subTree);return}const o=n.shapeFlag&4?Ms(n.component):n.el,s=i?null:o,{i:a,r:u}=e,c=t&&t.r,l=a.refs===xe?a.refs={}:a.refs,f=a.setupState,d=Ae(f),h=f===xe?Rh:v=>Ie(d,v);if(c!=null&&c!==u){if(dc(t),Be(c))l[c]=null,h(c)&&(f[c]=null);else if(Ve(c)){c.value=null;const v=t;v.k&&(l[v.k]=null)}}if(he(u))Zi(u,a,12,[s,l]);else{const v=Be(u),p=Ve(u);if(v||p){const b=()=>{if(e.f){const E=v?h(u)?f[u]:l[u]:u.value;if(i)ve(E)&&sl(E,o);else if(ve(E))E.includes(o)||E.push(o);else if(v)l[u]=[o],h(u)&&(f[u]=l[u]);else{const m=[o];u.value=m,e.k&&(l[e.k]=m)}}else v?(l[u]=s,h(u)&&(f[u]=s)):p&&(u.value=s,e.k&&(l[e.k]=s))};if(s){const E=()=>{b(),ss.delete(e)};E.id=-1,ss.set(e,E),Qe(E,r)}else dc(e),b()}}}function dc(e){const t=ss.get(e);t&&(t.flags|=8,ss.delete(e))}let hc=!1;const mn=()=>{hc||(console.error("Hydration completed but contains mismatches."),hc=!0)},a0=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",u0=e=>e.namespaceURI.includes("MathML"),fo=e=>{if(e.nodeType===1){if(a0(e))return"svg";if(u0(e))return"mathml"}},An=e=>e.nodeType===8;function l0(e){const{mt:t,p:r,o:{patchProp:n,createText:i,nextSibling:o,parentNode:s,remove:a,insert:u,createComment:c}}=e,l=(g,y)=>{if(!y.hasChildNodes()){r(null,g,y),is(),y._vnode=g;return}f(y.firstChild,g,null,null,null),is(),y._vnode=g},f=(g,y,_,S,A,C=!1)=>{C=C||!!y.dynamicChildren;const I=An(g)&&g.data==="[",P=()=>p(g,y,_,S,A,I),{type:R,ref:k,shapeFlag:N,patchFlag:$}=y;let ee=g.nodeType;y.el=g,$===-2&&(C=!1,y.dynamicChildren=null);let B=null;switch(R){case sn:ee!==3?y.children===""?(u(y.el=i(""),s(g),g),B=g):B=P():(g.data!==y.children&&(mn(),g.data=y.children),B=o(g));break;case Ye:m(g)?(B=o(g),E(y.el=g.content.firstChild,g,_)):ee!==8||I?B=P():B=o(g);break;case Ii:if(I&&(g=o(g),ee=g.nodeType),ee===1||ee===3){B=g;const z=!y.children.length;for(let K=0;K{C=C||!!y.dynamicChildren;const{type:I,props:P,patchFlag:R,shapeFlag:k,dirs:N,transition:$}=y,ee=I==="input"||I==="option";if(ee||R!==-1){N&&zt(y,null,_,"created");let B=!1;if(m(g)){B=Uv(null,$)&&_&&_.vnode.props&&_.vnode.props.appear;const K=g.content.firstChild;if(B){const Q=K.getAttribute("class");Q&&(K.$cls=Q),$.beforeEnter(K)}E(K,g,_),y.el=g=K}if(k&16&&!(P&&(P.innerHTML||P.textContent))){let K=h(g.firstChild,y,g,_,S,A,C);for(;K;){ho(g,1)||mn();const Q=K;K=K.nextSibling,a(Q)}}else if(k&8){let K=y.children;K[0]===` -`&&(g.tagName==="PRE"||g.tagName==="TEXTAREA")&&(K=K.slice(1));const{textContent:Q}=g;Q!==K&&Q!==K.replace(/\r\n|\r/g,` -`)&&(ho(g,0)||mn(),g.textContent=y.children)}if(P){if(ee||!C||R&48){const K=g.tagName.includes("-");for(const Q in P)(ee&&(Q.endsWith("value")||Q==="indeterminate")||Ji(Q)&&!tn(Q)||Q[0]==="."||K&&!tn(Q))&&n(g,Q,null,P[Q],void 0,_)}else if(P.onClick)n(g,"onClick",null,P.onClick,void 0,_);else if(R&4&&nn(P.style))for(const K in P.style)P.style[K]}let z;(z=P&&P.onVnodeBeforeMount)&&yt(z,_,y),N&&zt(y,null,_,"beforeMount"),((z=P&&P.onVnodeMounted)||N||B)&&Vv(()=>{z&&yt(z,_,y),B&&$.enter(g),N&&zt(y,null,_,"mounted")},S)}return g.nextSibling},h=(g,y,_,S,A,C,I)=>{I=I||!!y.dynamicChildren;const P=y.children,R=P.length;for(let k=0;k{const{slotScopeIds:I}=y;I&&(A=A?A.concat(I):I);const P=s(g),R=h(o(g),y,P,_,S,A,C);return R&&An(R)&&R.data==="]"?o(y.anchor=R):(mn(),u(y.anchor=c("]"),P,R),R)},p=(g,y,_,S,A,C)=>{if(ho(g.parentElement,1)||mn(),y.el=null,C){const R=b(g);for(;;){const k=o(g);if(k&&k!==R)a(k);else break}}const I=o(g),P=s(g);return a(g),r(null,y,P,I,_,S,fo(P),A),_&&(_.vnode.el=y.el,Ls(_,y.el)),I},b=(g,y="[",_="]")=>{let S=0;for(;g;)if(g=o(g),g&&An(g)&&(g.data===y&&S++,g.data===_)){if(S===0)return o(g);S--}return g},E=(g,y,_)=>{const S=y.parentNode;S&&S.replaceChild(g,y);let A=_;for(;A;)A.vnode.el===y&&(A.vnode.el=A.subTree.el=g),A=A.parent},m=g=>g.nodeType===1&&g.tagName==="TEMPLATE";return[l,f]}const vc="data-allow-mismatch",c0={0:"text",1:"children",2:"class",3:"style",4:"attribute"};function ho(e,t){if(t===0||t===1)for(;e&&!e.hasAttribute(vc);)e=e.parentElement;const r=e&&e.getAttribute(vc);if(r==null)return!1;if(r==="")return!0;{const n=r.split(",");return t===0&&n.includes("children")?!0:n.includes(c0[t])}}Is().requestIdleCallback;Is().cancelIdleCallback;function f0(e,t){if(An(e)&&e.data==="["){let r=1,n=e.nextSibling;for(;n;){if(n.nodeType===1){if(t(n)===!1)break}else if(An(n))if(n.data==="]"){if(--r===0)break}else n.data==="["&&r++;n=n.nextSibling}}else t(e)}const Ur=e=>!!e.type.__asyncLoader;function pc(e){he(e)&&(e={loader:e});const{loader:t,loadingComponent:r,errorComponent:n,delay:i=200,hydrate:o,timeout:s,suspensible:a=!0,onError:u}=e;let c=null,l,f=0;const d=()=>(f++,c=null,h()),h=()=>{let v;return c||(v=c=t().catch(p=>{if(p=p instanceof Error?p:new Error(String(p)),u)return new Promise((b,E)=>{u(p,()=>b(d()),()=>E(p),f+1)});throw p}).then(p=>v!==c&&c?c:(p&&(p.__esModule||p[Symbol.toStringTag]==="Module")&&(p=p.default),l=p,p)))};return jr({name:"AsyncComponentWrapper",__asyncLoader:h,__asyncHydrate(v,p,b){let E=!1;(p.bu||(p.bu=[])).push(()=>E=!0);const m=()=>{E||b()},g=o?()=>{const y=o(m,_=>f0(v,_));y&&(p.bum||(p.bum=[])).push(y)}:m;l?g():h().then(()=>!p.isUnmounted&&g())},get __asyncResolved(){return l},setup(){const v=it;if(bl(v),l)return()=>vo(l,v);const p=g=>{c=null,Zn(g,v,13,!n)};if(a&&v.suspense||Gn)return h().then(g=>()=>vo(g,v)).catch(g=>(p(g),()=>n?De(n,{error:g}):null));const b=Qt(!1),E=Qt(),m=Qt(!!i);return i&&setTimeout(()=>{m.value=!1},i),s!=null&&setTimeout(()=>{if(!b.value&&!E.value){const g=new Error(`Async component timed out after ${s}ms.`);p(g),E.value=g}},s),h().then(()=>{b.value=!0,v.parent&&eo(v.parent.vnode)&&v.parent.update()}).catch(g=>{p(g),E.value=g}),()=>{if(b.value&&l)return vo(l,v);if(E.value&&n)return De(n,{error:E.value});if(r&&!m.value)return vo(r,v)}}})}function vo(e,t){const{ref:r,props:n,children:i,ce:o}=t.vnode,s=De(e,n,i);return s.ref=r,s.ce=o,delete t.vnode.ce,s}const eo=e=>e.type.__isKeepAlive,d0={name:"KeepAlive",__isKeepAlive:!0,props:{include:[String,RegExp,Array],exclude:[String,RegExp,Array],max:[String,Number]},setup(e,{slots:t}){const r=hn(),n=r.ctx;if(!n.renderer)return()=>{const m=t.default&&t.default();return m&&m.length===1?m[0]:m};const i=new Map,o=new Set;let s=null;const a=r.suspense,{renderer:{p:u,m:c,um:l,o:{createElement:f}}}=n,d=f("div");n.activate=(m,g,y,_,S)=>{const A=m.component;c(m,g,y,0,a),u(A.vnode,m,g,y,A,a,_,m.slotScopeIds,S),Qe(()=>{A.isDeactivated=!1,A.a&&xn(A.a);const C=m.props&&m.props.onVnodeMounted;C&&yt(C,A.parent,m)},a)},n.deactivate=m=>{const g=m.component;us(g.m),us(g.a),c(m,d,null,1,a),Qe(()=>{g.da&&xn(g.da);const y=m.props&&m.props.onVnodeUnmounted;y&&yt(y,g.parent,m),g.isDeactivated=!0},a)};function h(m){ra(m),l(m,r,a,!0)}function v(m){i.forEach((g,y)=>{const _=su(Ur(g)?g.type.__asyncResolved||{}:g.type);_&&!m(_)&&p(y)})}function p(m){const g=i.get(m);g&&(!s||!Bt(g,s))?h(g):s&&ra(s),i.delete(m),o.delete(m)}Mn(()=>[e.include,e.exclude],([m,g])=>{m&&v(y=>bi(m,y)),g&&v(y=>!bi(g,y))},{flush:"post",deep:!0});let b=null;const E=()=>{b!=null&&(ls(r.subTree.type)?Qe(()=>{i.set(b,po(r.subTree))},r.subTree.suspense):i.set(b,po(r.subTree)))};return Vn(E),yv(E),ei(()=>{i.forEach(m=>{const{subTree:g,suspense:y}=r,_=po(g);if(m.type===_.type&&m.key===_.key){ra(_);const S=_.component.da;S&&Qe(S,y);return}h(m)})}),()=>{if(b=null,!t.default)return s=null;const m=t.default(),g=m[0];if(m.length>1)return s=null,m;if(!Wn(g)||!(g.shapeFlag&4)&&!(g.shapeFlag&128))return s=null,g;let y=po(g);if(y.type===Ye)return s=null,y;const _=y.type,S=su(Ur(y)?y.type.__asyncResolved||{}:_),{include:A,exclude:C,max:I}=e;if(A&&(!S||!bi(A,S))||C&&S&&bi(C,S))return y.shapeFlag&=-257,s=y,g;const P=y.key==null?_:y.key,R=i.get(P);return y.el&&(y=xt(y),g.shapeFlag&128&&(g.ssContent=y)),b=P,R?(y.el=R.el,y.component=R.component,y.transition&&qn(y,y.transition),y.shapeFlag|=512,o.delete(P),o.add(P)):(o.add(P),I&&o.size>parseInt(I,10)&&p(o.values().next().value)),y.shapeFlag|=256,s=y,ls(g.type)?g:y}}},h0=d0;function bi(e,t){return ve(e)?e.some(r=>bi(r,t)):Be(e)?e.split(",").includes(t):uy(e)?(e.lastIndex=0,e.test(t)):!1}function pv(e,t){mv(e,"a",t)}function gv(e,t){mv(e,"da",t)}function mv(e,t,r=it){const n=e.__wdc||(e.__wdc=()=>{let i=r;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(xs(t,n,r),r){let i=r.parent;for(;i&&i.parent;)eo(i.parent.vnode)&&v0(n,t,r,i),i=i.parent}}function v0(e,t,r,n){const i=xs(t,e,n,!0);bv(()=>{sl(n[t],i)},r)}function ra(e){e.shapeFlag&=-257,e.shapeFlag&=-513}function po(e){return e.shapeFlag&128?e.ssContent:e}function xs(e,t,r=it,n=!1){if(r){const i=r[e]||(r[e]=[]),o=t.__weh||(t.__weh=(...s)=>{pr();const a=to(r),u=qt(t,r,e,s);return a(),gr(),u});return n?i.unshift(o):i.push(o),o}}const Er=e=>(t,r=it)=>{(!Gn||e==="sp")&&xs(e,(...n)=>t(...n),r)},p0=Er("bm"),Vn=Er("m"),g0=Er("bu"),yv=Er("u"),ei=Er("bum"),bv=Er("um"),m0=Er("sp"),y0=Er("rtg"),b0=Er("rtc");function Ev(e,t=it){xs("ec",e,t)}const _v="components";function gc(e,t){return wv(_v,e,!0,t)||e}const Sv=Symbol.for("v-ndc");function E0(e){return Be(e)?wv(_v,e,!1)||e:e||Sv}function wv(e,t,r=!0,n=!1){const i=Ct||it;if(i){const o=i.type;{const a=su(o,!1);if(a&&(a===t||a===Mt(t)||a===Cs(Mt(t))))return o}const s=mc(i[e]||o[e],t)||mc(i.appContext[e],t);return!s&&n?o:s}}function mc(e,t){return e&&(e[t]||e[Mt(t)]||e[Cs(Mt(t))])}const Za=e=>e?zv(e)?Ms(e):Za(e.parent):null,Ci=Je(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Za(e.parent),$root:e=>Za(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>Av(e),$forceUpdate:e=>e.f||(e.f=()=>{ml(e.update)}),$nextTick:e=>e.n||(e.n=Vi.bind(e.proxy)),$watch:e=>r0.bind(e)}),na=(e,t)=>e!==xe&&!e.__isScriptSetup&&Ie(e,t),_0={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:r,setupState:n,data:i,props:o,accessCache:s,type:a,appContext:u}=e;if(t[0]!=="$"){const d=s[t];if(d!==void 0)switch(d){case 1:return n[t];case 2:return i[t];case 4:return r[t];case 3:return o[t]}else{if(na(n,t))return s[t]=1,n[t];if(i!==xe&&Ie(i,t))return s[t]=2,i[t];if(Ie(o,t))return s[t]=3,o[t];if(r!==xe&&Ie(r,t))return s[t]=4,r[t];eu&&(s[t]=0)}}const c=Ci[t];let l,f;if(c)return t==="$attrs"&&ut(e.attrs,"get",""),c(e);if((l=a.__cssModules)&&(l=l[t]))return l;if(r!==xe&&Ie(r,t))return s[t]=4,r[t];if(f=u.config.globalProperties,Ie(f,t))return f[t]},set({_:e},t,r){const{data:n,setupState:i,ctx:o}=e;return na(i,t)?(i[t]=r,!0):n!==xe&&Ie(n,t)?(n[t]=r,!0):Ie(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=r,!0)},has({_:{data:e,setupState:t,accessCache:r,ctx:n,appContext:i,props:o,type:s}},a){let u;return!!(r[a]||e!==xe&&a[0]!=="$"&&Ie(e,a)||na(t,a)||Ie(o,a)||Ie(n,a)||Ie(Ci,a)||Ie(i.config.globalProperties,a)||(u=s.__cssModules)&&u[a])},defineProperty(e,t,r){return r.get!=null?e._.accessCache[t]=0:Ie(r,"value")&&this.set(e,t,r.value,null),Reflect.defineProperty(e,t,r)}};function yc(e){return ve(e)?e.reduce((t,r)=>(t[r]=null,t),{}):e}let eu=!0;function S0(e){const t=Av(e),r=e.proxy,n=e.ctx;eu=!1,t.beforeCreate&&bc(t.beforeCreate,e,"bc");const{data:i,computed:o,methods:s,watch:a,provide:u,inject:c,created:l,beforeMount:f,mounted:d,beforeUpdate:h,updated:v,activated:p,deactivated:b,beforeDestroy:E,beforeUnmount:m,destroyed:g,unmounted:y,render:_,renderTracked:S,renderTriggered:A,errorCaptured:C,serverPrefetch:I,expose:P,inheritAttrs:R,components:k,directives:N,filters:$}=t;if(c&&w0(c,n,null),s)for(const z in s){const K=s[z];he(K)&&(n[z]=K.bind(r))}if(i){const z=i.call(r,r);Ue(z)&&(e.data=Br(z))}if(eu=!0,o)for(const z in o){const K=o[z],Q=he(K)?K.bind(r,r):he(K.get)?K.get.bind(r,r):Jt,ae=!he(K)&&he(K.set)?K.set.bind(r):Jt,fe=We({get:Q,set:ae});Object.defineProperty(n,z,{enumerable:!0,configurable:!0,get:()=>fe.value,set:pe=>fe.value=pe})}if(a)for(const z in a)Tv(a[z],n,r,z);if(u){const z=he(u)?u.call(r):u;Reflect.ownKeys(z).forEach(K=>{Ln(K,z[K])})}l&&bc(l,e,"c");function B(z,K){ve(K)?K.forEach(Q=>z(Q.bind(r))):K&&z(K.bind(r))}if(B(p0,f),B(Vn,d),B(g0,h),B(yv,v),B(pv,p),B(gv,b),B(Ev,C),B(b0,S),B(y0,A),B(ei,m),B(bv,y),B(m0,I),ve(P))if(P.length){const z=e.exposed||(e.exposed={});P.forEach(K=>{Object.defineProperty(z,K,{get:()=>r[K],set:Q=>r[K]=Q,enumerable:!0})})}else e.exposed||(e.exposed={});_&&e.render===Jt&&(e.render=_),R!=null&&(e.inheritAttrs=R),k&&(e.components=k),N&&(e.directives=N),I&&bl(e)}function w0(e,t,r=Jt){ve(e)&&(e=tu(e));for(const n in e){const i=e[n];let o;Ue(i)?"default"in i?o=_t(i.from||n,i.default,!0):o=_t(i.from||n):o=_t(i),Ve(o)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>o.value,set:s=>o.value=s}):t[n]=o}}function bc(e,t,r){qt(ve(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,r)}function Tv(e,t,r,n){let i=n.includes(".")?uv(r,n):()=>r[n];if(Be(e)){const o=t[e];he(o)&&Mn(i,o)}else if(he(e))Mn(i,e.bind(r));else if(Ue(e))if(ve(e))e.forEach(o=>Tv(o,t,r,n));else{const o=he(e.handler)?e.handler.bind(r):t[e.handler];he(o)&&Mn(i,o,e)}}function Av(e){const t=e.type,{mixins:r,extends:n}=t,{mixins:i,optionsCache:o,config:{optionMergeStrategies:s}}=e.appContext,a=o.get(t);let u;return a?u=a:!i.length&&!r&&!n?u=t:(u={},i.length&&i.forEach(c=>as(u,c,s,!0)),as(u,t,s)),Ue(t)&&o.set(t,u),u}function as(e,t,r,n=!1){const{mixins:i,extends:o}=t;o&&as(e,o,r,!0),i&&i.forEach(s=>as(e,s,r,!0));for(const s in t)if(!(n&&s==="expose")){const a=T0[s]||r&&r[s];e[s]=a?a(e[s],t[s]):t[s]}return e}const T0={data:Ec,props:_c,emits:_c,methods:Ei,computed:Ei,beforeCreate:dt,created:dt,beforeMount:dt,mounted:dt,beforeUpdate:dt,updated:dt,beforeDestroy:dt,beforeUnmount:dt,destroyed:dt,unmounted:dt,activated:dt,deactivated:dt,errorCaptured:dt,serverPrefetch:dt,components:Ei,directives:Ei,watch:C0,provide:Ec,inject:A0};function Ec(e,t){return t?e?function(){return Je(he(e)?e.call(this,this):e,he(t)?t.call(this,this):t)}:t:e}function A0(e,t){return Ei(tu(e),tu(t))}function tu(e){if(ve(e)){const t={};for(let r=0;rt==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${Mt(t)}Modifiers`]||e[`${dn(t)}Modifiers`];function R0(e,t,...r){if(e.isUnmounted)return;const n=e.vnode.props||xe;let i=r;const o=t.startsWith("update:"),s=o&&P0(n,t.slice(7));s&&(s.trim&&(i=r.map(l=>Be(l)?l.trim():l)),s.number&&(i=r.map(ul)));let a,u=n[a=Ys(t)]||n[a=Ys(Mt(t))];!u&&o&&(u=n[a=Ys(dn(t))]),u&&qt(u,e,6,i);const c=n[a+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[a])return;e.emitted[a]=!0,qt(c,e,6,i)}}const O0=new WeakMap;function Iv(e,t,r=!1){const n=r?O0:t.emitsCache,i=n.get(e);if(i!==void 0)return i;const o=e.emits;let s={},a=!1;if(!he(e)){const u=c=>{const l=Iv(c,t,!0);l&&(a=!0,Je(s,l))};!r&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}return!o&&!a?(Ue(e)&&n.set(e,null),null):(ve(o)?o.forEach(u=>s[u]=null):Je(s,o),Ue(e)&&n.set(e,s),s)}function Ns(e,t){return!e||!Ji(t)?!1:(t=t.slice(2).replace(/Once$/,""),Ie(e,t[0].toLowerCase()+t.slice(1))||Ie(e,dn(t))||Ie(e,t))}function ia(e){const{type:t,vnode:r,proxy:n,withProxy:i,propsOptions:[o],slots:s,attrs:a,emit:u,render:c,renderCache:l,props:f,data:d,setupState:h,ctx:v,inheritAttrs:p}=e,b=os(e);let E,m;try{if(r.shapeFlag&4){const y=i||n,_=y;E=At(c.call(_,y,l,f,h,d,v)),m=a}else{const y=t;E=At(y.length>1?y(f,{attrs:a,slots:s,emit:u}):y(f,null)),m=t.props?a:N0(a)}}catch(y){ki.length=0,Zn(y,e,1),E=De(Ye)}let g=E;if(m&&p!==!1){const y=Object.keys(m),{shapeFlag:_}=g;y.length&&_&7&&(o&&y.some(ol)&&(m=L0(m,o)),g=xt(g,m,!1,!0))}return r.dirs&&(g=xt(g,null,!1,!0),g.dirs=g.dirs?g.dirs.concat(r.dirs):r.dirs),r.transition&&qn(g,r.transition),E=g,os(b),E}function x0(e,t=!0){let r;for(let n=0;n{let t;for(const r in e)(r==="class"||r==="style"||Ji(r))&&((t||(t={}))[r]=e[r]);return t},L0=(e,t)=>{const r={};for(const n in e)(!ol(n)||!(n.slice(9)in t))&&(r[n]=e[n]);return r};function M0(e,t,r){const{props:n,children:i,component:o}=e,{props:s,children:a,patchFlag:u}=t,c=o.emitsOptions;if(t.dirs||t.transition)return!0;if(r&&u>=0){if(u&1024)return!0;if(u&16)return n?Sc(n,s,c):!!s;if(u&8){const l=t.dynamicProps;for(let f=0;fObject.create(kv),Rv=e=>Object.getPrototypeOf(e)===kv;function D0(e,t,r,n=!1){const i={},o=Pv();e.propsDefaults=Object.create(null),Ov(e,t,i,o);for(const s in e.propsOptions[0])s in i||(i[s]=void 0);r?e.props=n?i:hr(i):e.type.props?e.props=i:e.props=o,e.attrs=o}function U0(e,t,r,n){const{props:i,attrs:o,vnode:{patchFlag:s}}=e,a=Ae(i),[u]=e.propsOptions;let c=!1;if((n||s>0)&&!(s&16)){if(s&8){const l=e.vnode.dynamicProps;for(let f=0;f{u=!0;const[d,h]=xv(f,t,!0);Je(s,d),h&&a.push(...h)};!r&&t.mixins.length&&t.mixins.forEach(l),e.extends&&l(e.extends),e.mixins&&e.mixins.forEach(l)}if(!o&&!u)return Ue(e)&&n.set(e,Rn),Rn;if(ve(o))for(let l=0;le==="_"||e==="_ctx"||e==="$stable",_l=e=>ve(e)?e.map(At):[At(e)],H0=(e,t,r)=>{if(t._n)return t;const n=Ai((...i)=>_l(t(...i)),r);return n._c=!1,n},Nv=(e,t,r)=>{const n=e._ctx;for(const i in e){if(El(i))continue;const o=e[i];if(he(o))t[i]=H0(i,o,n);else if(o!=null){const s=_l(o);t[i]=()=>s}}},Lv=(e,t)=>{const r=_l(t);e.slots.default=()=>r},Mv=(e,t,r)=>{for(const n in t)(r||!El(n))&&(e[n]=t[n])},B0=(e,t,r)=>{const n=e.slots=Pv();if(e.vnode.shapeFlag&32){const i=t._;i?(Mv(n,t,r),r&&Lh(n,"_",i,!0)):Nv(t,n)}else t&&Lv(e,t)},j0=(e,t,r)=>{const{vnode:n,slots:i}=e;let o=!0,s=xe;if(n.shapeFlag&32){const a=t._;a?r&&a===1?o=!1:Mv(i,t,r):(o=!t.$stable,Nv(t,i)),s=t}else t&&(Lv(e,t),s={default:1});if(o)for(const a in i)!El(a)&&s[a]==null&&delete i[a]},Qe=Vv;function q0(e){return Dv(e)}function V0(e){return Dv(e,l0)}function Dv(e,t){const r=Is();r.__VUE__=!0;const{insert:n,remove:i,patchProp:o,createElement:s,createText:a,createComment:u,setText:c,setElementText:l,parentNode:f,nextSibling:d,setScopeId:h=Jt,insertStaticContent:v}=e,p=(w,T,L,H=null,U=null,q=null,te=void 0,G=null,Y=!!T.dynamicChildren)=>{if(w===T)return;w&&!Bt(w,T)&&(H=M(w),pe(w,U,q,!0),w=null),T.patchFlag===-2&&(Y=!1,T.dynamicChildren=null);const{type:V,ref:W,shapeFlag:j}=T;switch(V){case sn:b(w,T,L,H);break;case Ye:E(w,T,L,H);break;case Ii:w==null&&m(T,L,H,te);break;case lt:k(w,T,L,H,U,q,te,G,Y);break;default:j&1?_(w,T,L,H,U,q,te,G,Y):j&6?N(w,T,L,H,U,q,te,G,Y):(j&64||j&128)&&V.process(w,T,L,H,U,q,te,G,Y,re)}W!=null&&U?Dn(W,w&&w.ref,q,T||w,!T):W==null&&w&&w.ref!=null&&Dn(w.ref,null,q,w,!0)},b=(w,T,L,H)=>{if(w==null)n(T.el=a(T.children),L,H);else{const U=T.el=w.el;T.children!==w.children&&c(U,T.children)}},E=(w,T,L,H)=>{w==null?n(T.el=u(T.children||""),L,H):T.el=w.el},m=(w,T,L,H)=>{[w.el,w.anchor]=v(w.children,T,L,H,w.el,w.anchor)},g=({el:w,anchor:T},L,H)=>{let U;for(;w&&w!==T;)U=d(w),n(w,L,H),w=U;n(T,L,H)},y=({el:w,anchor:T})=>{let L;for(;w&&w!==T;)L=d(w),i(w),w=L;i(T)},_=(w,T,L,H,U,q,te,G,Y)=>{if(T.type==="svg"?te="svg":T.type==="math"&&(te="mathml"),w==null)S(T,L,H,U,q,te,G,Y);else{const V=w.el&&w.el._isVueCE?w.el:null;try{V&&V._beginPatch(),I(w,T,U,q,te,G,Y)}finally{V&&V._endPatch()}}},S=(w,T,L,H,U,q,te,G)=>{let Y,V;const{props:W,shapeFlag:j,transition:J,dirs:ne}=w;if(Y=w.el=s(w.type,q,W&&W.is,W),j&8?l(Y,w.children):j&16&&C(w.children,Y,null,H,U,oa(w,q),te,G),ne&&zt(w,null,H,"created"),A(Y,w,w.scopeId,te,H),W){for(const Se in W)Se!=="value"&&!tn(Se)&&o(Y,Se,null,W[Se],q,H);"value"in W&&o(Y,"value",null,W.value,q),(V=W.onVnodeBeforeMount)&&yt(V,H,w)}ne&&zt(w,null,H,"beforeMount");const ie=Uv(U,J);ie&&J.beforeEnter(Y),n(Y,T,L),((V=W&&W.onVnodeMounted)||ie||ne)&&Qe(()=>{V&&yt(V,H,w),ie&&J.enter(Y),ne&&zt(w,null,H,"mounted")},U)},A=(w,T,L,H,U)=>{if(L&&h(w,L),H)for(let q=0;q{for(let V=Y;V{const G=T.el=w.el;let{patchFlag:Y,dynamicChildren:V,dirs:W}=T;Y|=w.patchFlag&16;const j=w.props||xe,J=T.props||xe;let ne;if(L&&Vr(L,!1),(ne=J.onVnodeBeforeUpdate)&&yt(ne,L,T,w),W&&zt(T,w,L,"beforeUpdate"),L&&Vr(L,!0),(j.innerHTML&&J.innerHTML==null||j.textContent&&J.textContent==null)&&l(G,""),V?P(w.dynamicChildren,V,G,L,H,oa(T,U),q):te||K(w,T,G,null,L,H,oa(T,U),q,!1),Y>0){if(Y&16)R(G,j,J,L,U);else if(Y&2&&j.class!==J.class&&o(G,"class",null,J.class,U),Y&4&&o(G,"style",j.style,J.style,U),Y&8){const ie=T.dynamicProps;for(let Se=0;Se{ne&&yt(ne,L,T,w),W&&zt(T,w,L,"updated")},H)},P=(w,T,L,H,U,q,te)=>{for(let G=0;G{if(T!==L){if(T!==xe)for(const q in T)!tn(q)&&!(q in L)&&o(w,q,T[q],null,U,H);for(const q in L){if(tn(q))continue;const te=L[q],G=T[q];te!==G&&q!=="value"&&o(w,q,G,te,U,H)}"value"in L&&o(w,"value",T.value,L.value,U)}},k=(w,T,L,H,U,q,te,G,Y)=>{const V=T.el=w?w.el:a(""),W=T.anchor=w?w.anchor:a("");let{patchFlag:j,dynamicChildren:J,slotScopeIds:ne}=T;ne&&(G=G?G.concat(ne):ne),w==null?(n(V,L,H),n(W,L,H),C(T.children||[],L,W,U,q,te,G,Y)):j>0&&j&64&&J&&w.dynamicChildren&&w.dynamicChildren.length===J.length?(P(w.dynamicChildren,J,L,U,q,te,G),(T.key!=null||U&&T===U.subTree)&&Fv(w,T,!0)):K(w,T,L,W,U,q,te,G,Y)},N=(w,T,L,H,U,q,te,G,Y)=>{T.slotScopeIds=G,w==null?T.shapeFlag&512?U.ctx.activate(T,L,H,te,Y):$(T,L,H,U,q,te,Y):ee(w,T,Y)},$=(w,T,L,H,U,q,te)=>{const G=w.component=nb(w,H,U);if(eo(w)&&(G.ctx.renderer=re),ib(G,!1,te),G.asyncDep){if(U&&U.registerDep(G,B,te),!w.el){const Y=G.subTree=De(Ye);E(null,Y,T,L),w.placeholder=Y.el}}else B(G,w,T,L,U,q,te)},ee=(w,T,L)=>{const H=T.component=w.component;if(M0(w,T,L))if(H.asyncDep&&!H.asyncResolved){z(H,T,L);return}else H.next=T,H.update();else T.el=w.el,H.vnode=T},B=(w,T,L,H,U,q,te)=>{const G=()=>{if(w.isMounted){let{next:j,bu:J,u:ne,parent:ie,vnode:Se}=w;{const ot=Hv(w);if(ot){j&&(j.el=Se.el,z(w,j,te)),ot.asyncDep.then(()=>{w.isUnmounted||G()});return}}let ue=j,$e;Vr(w,!1),j?(j.el=Se.el,z(w,j,te)):j=Se,J&&xn(J),($e=j.props&&j.props.onVnodeBeforeUpdate)&&yt($e,ie,j,Se),Vr(w,!0);const Oe=ia(w),mt=w.subTree;w.subTree=Oe,p(mt,Oe,f(mt.el),M(mt),w,U,q),j.el=Oe.el,ue===null&&Ls(w,Oe.el),ne&&Qe(ne,U),($e=j.props&&j.props.onVnodeUpdated)&&Qe(()=>yt($e,ie,j,Se),U)}else{let j;const{el:J,props:ne}=T,{bm:ie,m:Se,parent:ue,root:$e,type:Oe}=w,mt=Ur(T);if(Vr(w,!1),ie&&xn(ie),!mt&&(j=ne&&ne.onVnodeBeforeMount)&&yt(j,ue,T),Vr(w,!0),J&&ge){const ot=()=>{w.subTree=ia(w),ge(J,w.subTree,w,U,null)};mt&&Oe.__asyncHydrate?Oe.__asyncHydrate(J,w,ot):ot()}else{$e.ce&&$e.ce._def.shadowRoot!==!1&&$e.ce._injectChildStyle(Oe);const ot=w.subTree=ia(w);p(null,ot,L,H,w,U,q),T.el=ot.el}if(Se&&Qe(Se,U),!mt&&(j=ne&&ne.onVnodeMounted)){const ot=T;Qe(()=>yt(j,ue,ot),U)}(T.shapeFlag&256||ue&&Ur(ue.vnode)&&ue.vnode.shapeFlag&256)&&w.a&&Qe(w.a,U),w.isMounted=!0,T=L=H=null}};w.scope.on();const Y=w.effect=new jh(G);w.scope.off();const V=w.update=Y.run.bind(Y),W=w.job=Y.runIfDirty.bind(Y);W.i=w,W.id=w.uid,Y.scheduler=()=>ml(W),Vr(w,!0),V()},z=(w,T,L)=>{T.component=w;const H=w.vnode.props;w.vnode=T,w.next=null,U0(w,T.props,H,L),j0(w,T.children,L),pr(),cc(w),gr()},K=(w,T,L,H,U,q,te,G,Y=!1)=>{const V=w&&w.children,W=w?w.shapeFlag:0,j=T.children,{patchFlag:J,shapeFlag:ne}=T;if(J>0){if(J&128){ae(V,j,L,H,U,q,te,G,Y);return}else if(J&256){Q(V,j,L,H,U,q,te,G,Y);return}}ne&8?(W&16&&me(V,U,q),j!==V&&l(L,j)):W&16?ne&16?ae(V,j,L,H,U,q,te,G,Y):me(V,U,q,!0):(W&8&&l(L,""),ne&16&&C(j,L,H,U,q,te,G,Y))},Q=(w,T,L,H,U,q,te,G,Y)=>{w=w||Rn,T=T||Rn;const V=w.length,W=T.length,j=Math.min(V,W);let J;for(J=0;JW?me(w,U,q,!0,!1,j):C(T,L,H,U,q,te,G,Y,j)},ae=(w,T,L,H,U,q,te,G,Y)=>{let V=0;const W=T.length;let j=w.length-1,J=W-1;for(;V<=j&&V<=J;){const ne=w[V],ie=T[V]=Y?Cr(T[V]):At(T[V]);if(Bt(ne,ie))p(ne,ie,L,null,U,q,te,G,Y);else break;V++}for(;V<=j&&V<=J;){const ne=w[j],ie=T[J]=Y?Cr(T[J]):At(T[J]);if(Bt(ne,ie))p(ne,ie,L,null,U,q,te,G,Y);else break;j--,J--}if(V>j){if(V<=J){const ne=J+1,ie=neJ)for(;V<=j;)pe(w[V],U,q,!0),V++;else{const ne=V,ie=V,Se=new Map;for(V=ie;V<=J;V++){const Tt=T[V]=Y?Cr(T[V]):At(T[V]);Tt.key!=null&&Se.set(Tt.key,V)}let ue,$e=0;const Oe=J-ie+1;let mt=!1,ot=0;const ii=new Array(Oe);for(V=0;V=Oe){pe(Tt,U,q,!0);continue}let Wt;if(Tt.key!=null)Wt=Se.get(Tt.key);else for(ue=ie;ue<=J;ue++)if(ii[ue-ie]===0&&Bt(Tt,T[ue])){Wt=ue;break}Wt===void 0?pe(Tt,U,q,!0):(ii[Wt-ie]=V+1,Wt>=ot?ot=Wt:mt=!0,p(Tt,T[Wt],L,null,U,q,te,G,Y),$e++)}const ic=mt?$0(ii):Rn;for(ue=ic.length-1,V=Oe-1;V>=0;V--){const Tt=ie+V,Wt=T[Tt],oc=T[Tt+1],sc=Tt+1{const{el:q,type:te,transition:G,children:Y,shapeFlag:V}=w;if(V&6){fe(w.component.subTree,T,L,H);return}if(V&128){w.suspense.move(T,L,H);return}if(V&64){te.move(w,T,L,re);return}if(te===lt){n(q,T,L);for(let j=0;jG.enter(q),U);else{const{leave:j,delayLeave:J,afterLeave:ne}=G,ie=()=>{w.ctx.isUnmounted?i(q):n(q,T,L)},Se=()=>{q._isLeaving&&q[ur](!0),j(q,()=>{ie(),ne&&ne()})};J?J(q,ie,Se):Se()}else n(q,T,L)},pe=(w,T,L,H=!1,U=!1)=>{const{type:q,props:te,ref:G,children:Y,dynamicChildren:V,shapeFlag:W,patchFlag:j,dirs:J,cacheIndex:ne}=w;if(j===-2&&(U=!1),G!=null&&(pr(),Dn(G,null,L,w,!0),gr()),ne!=null&&(T.renderCache[ne]=void 0),W&256){T.ctx.deactivate(w);return}const ie=W&1&&J,Se=!Ur(w);let ue;if(Se&&(ue=te&&te.onVnodeBeforeUnmount)&&yt(ue,T,w),W&6)de(w.component,L,H);else{if(W&128){w.suspense.unmount(L,H);return}ie&&zt(w,null,T,"beforeUnmount"),W&64?w.type.remove(w,T,L,re,H):V&&!V.hasOnce&&(q!==lt||j>0&&j&64)?me(V,T,L,!1,!0):(q===lt&&j&384||!U&&W&16)&&me(Y,T,L),H&&ye(w)}(Se&&(ue=te&&te.onVnodeUnmounted)||ie)&&Qe(()=>{ue&&yt(ue,T,w),ie&&zt(w,null,T,"unmounted")},L)},ye=w=>{const{type:T,el:L,anchor:H,transition:U}=w;if(T===lt){be(L,H);return}if(T===Ii){y(w);return}const q=()=>{i(L),U&&!U.persisted&&U.afterLeave&&U.afterLeave()};if(w.shapeFlag&1&&U&&!U.persisted){const{leave:te,delayLeave:G}=U,Y=()=>te(L,q);G?G(w.el,q,Y):Y()}else q()},be=(w,T)=>{let L;for(;w!==T;)L=d(w),i(w),w=L;i(T)},de=(w,T,L)=>{const{bum:H,scope:U,job:q,subTree:te,um:G,m:Y,a:V}=w;us(Y),us(V),H&&xn(H),U.stop(),q&&(q.flags|=8,pe(te,w,T,L)),G&&Qe(G,T),Qe(()=>{w.isUnmounted=!0},T)},me=(w,T,L,H=!1,U=!1,q=0)=>{for(let te=q;te{if(w.shapeFlag&6)return M(w.component.subTree);if(w.shapeFlag&128)return w.suspense.next();const T=d(w.anchor||w.el),L=T&&T[n0];return L?d(L):T};let Z=!1;const X=(w,T,L)=>{let H;w==null?T._vnode&&(pe(T._vnode,null,null,!0),H=T._vnode.component):p(T._vnode||null,w,T,null,null,null,L),T._vnode=w,Z||(Z=!0,cc(H),is(),Z=!1)},re={p,um:pe,m:fe,r:ye,mt:$,mc:C,pc:K,pbc:P,n:M,o:e};let F,ge;return t&&([F,ge]=t(re)),{render:X,hydrate:F,createApp:k0(X,F)}}function oa({type:e,props:t},r){return r==="svg"&&e==="foreignObject"||r==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:r}function Vr({effect:e,job:t},r){r?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Uv(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function Fv(e,t,r=!1){const n=e.children,i=t.children;if(ve(n)&&ve(i))for(let o=0;o>1,e[r[a]]0&&(t[n]=r[o-1]),r[o]=n)}}for(o=r.length,s=r[o-1];o-- >0;)r[o]=s,s=t[s];return r}function Hv(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Hv(t)}function us(e){if(e)for(let t=0;te.__isSuspense;let nu=0;const W0={name:"Suspense",__isSuspense:!0,process(e,t,r,n,i,o,s,a,u,c){if(e==null)G0(t,r,n,i,o,s,a,u,c);else{if(o&&o.deps>0&&!e.suspense.isInFallback){t.suspense=e.suspense,t.suspense.vnode=t,t.el=e.el;return}K0(e,t,r,n,i,s,a,u,c)}},hydrate:z0,normalize:X0},jv=W0;function Wi(e,t){const r=e.props&&e.props[t];he(r)&&r()}function G0(e,t,r,n,i,o,s,a,u){const{p:c,o:{createElement:l}}=u,f=l("div"),d=e.suspense=qv(e,i,n,t,f,r,o,s,a,u);c(null,d.pendingBranch=e.ssContent,f,null,n,d,o,s),d.deps>0?(Wi(e,"onPending"),Wi(e,"onFallback"),c(null,e.ssFallback,t,r,n,null,o,s),Un(d,e.ssFallback)):d.resolve(!1,!0)}function K0(e,t,r,n,i,o,s,a,{p:u,um:c,o:{createElement:l}}){const f=t.suspense=e.suspense;f.vnode=t,t.el=e.el;const d=t.ssContent,h=t.ssFallback,{activeBranch:v,pendingBranch:p,isInFallback:b,isHydrating:E}=f;if(p)f.pendingBranch=d,Bt(p,d)?(u(p,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0?f.resolve():b&&(E||(u(v,h,r,n,i,null,o,s,a),Un(f,h)))):(f.pendingId=nu++,E?(f.isHydrating=!1,f.activeBranch=p):c(p,i,f),f.deps=0,f.effects.length=0,f.hiddenContainer=l("div"),b?(u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0?f.resolve():(u(v,h,r,n,i,null,o,s,a),Un(f,h))):v&&Bt(v,d)?(u(v,d,r,n,i,f,o,s,a),f.resolve(!0)):(u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0&&f.resolve()));else if(v&&Bt(v,d))u(v,d,r,n,i,f,o,s,a),Un(f,d);else if(Wi(t,"onPending"),f.pendingBranch=d,d.shapeFlag&512?f.pendingId=d.component.suspenseId:f.pendingId=nu++,u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0)f.resolve();else{const{timeout:m,pendingId:g}=f;m>0?setTimeout(()=>{f.pendingId===g&&f.fallback(h)},m):m===0&&f.fallback(h)}}function qv(e,t,r,n,i,o,s,a,u,c,l=!1){const{p:f,m:d,um:h,n:v,o:{parentNode:p,remove:b}}=c;let E;const m=Y0(e);m&&t&&t.pendingBranch&&(E=t.pendingId,t.deps++);const g=e.props?Mh(e.props.timeout):void 0,y=o,_={vnode:e,parent:t,parentComponent:r,namespace:s,container:n,hiddenContainer:i,deps:0,pendingId:nu++,timeout:typeof g=="number"?g:-1,activeBranch:null,pendingBranch:null,isInFallback:!l,isHydrating:l,isUnmounted:!1,effects:[],resolve(S=!1,A=!1){const{vnode:C,activeBranch:I,pendingBranch:P,pendingId:R,effects:k,parentComponent:N,container:$,isInFallback:ee}=_;let B=!1;_.isHydrating?_.isHydrating=!1:S||(B=I&&P.transition&&P.transition.mode==="out-in",B&&(I.transition.afterLeave=()=>{R===_.pendingId&&(d(P,$,o===y?v(I):o,0),Ja(k),ee&&C.ssFallback&&(C.ssFallback.el=null))}),I&&(p(I.el)===$&&(o=v(I)),h(I,N,_,!0),!B&&ee&&C.ssFallback&&Qe(()=>C.ssFallback.el=null,_)),B||d(P,$,o,0)),Un(_,P),_.pendingBranch=null,_.isInFallback=!1;let z=_.parent,K=!1;for(;z;){if(z.pendingBranch){z.effects.push(...k),K=!0;break}z=z.parent}!K&&!B&&Ja(k),_.effects=[],m&&t&&t.pendingBranch&&E===t.pendingId&&(t.deps--,t.deps===0&&!A&&t.resolve()),Wi(C,"onResolve")},fallback(S){if(!_.pendingBranch)return;const{vnode:A,activeBranch:C,parentComponent:I,container:P,namespace:R}=_;Wi(A,"onFallback");const k=v(C),N=()=>{_.isInFallback&&(f(null,S,P,k,I,null,R,a,u),Un(_,S))},$=S.transition&&S.transition.mode==="out-in";$&&(C.transition.afterLeave=N),_.isInFallback=!0,h(C,I,null,!0),$||N()},move(S,A,C){_.activeBranch&&d(_.activeBranch,S,A,C),_.container=S},next(){return _.activeBranch&&v(_.activeBranch)},registerDep(S,A,C){const I=!!_.pendingBranch;I&&_.deps++;const P=S.vnode.el;S.asyncDep.catch(R=>{Zn(R,S,0)}).then(R=>{if(S.isUnmounted||_.isUnmounted||_.pendingId!==S.suspenseId)return;S.asyncResolved=!0;const{vnode:k}=S;ou(S,R),P&&(k.el=P);const N=!P&&S.subTree.el;A(S,k,p(P||S.subTree.el),P?null:v(S.subTree),_,s,C),N&&(k.placeholder=null,b(N)),Ls(S,k.el),I&&--_.deps===0&&_.resolve()})},unmount(S,A){_.isUnmounted=!0,_.activeBranch&&h(_.activeBranch,r,S,A),_.pendingBranch&&h(_.pendingBranch,r,S,A)}};return _}function z0(e,t,r,n,i,o,s,a,u){const c=t.suspense=qv(t,n,r,e.parentNode,document.createElement("div"),null,i,o,s,a,!0),l=u(e,c.pendingBranch=t.ssContent,r,c,o,s);return c.deps===0&&c.resolve(!1,!0),l}function X0(e){const{shapeFlag:t,children:r}=e,n=t&32;e.ssContent=Tc(n?r.default:r),e.ssFallback=n?Tc(r.fallback):De(Ye)}function Tc(e){let t;if(he(e)){const r=$n&&e._c;r&&(e._d=!1,bt()),e=e(),r&&(e._d=!0,t=Et,$v())}return ve(e)&&(e=x0(e)),e=At(e),t&&!e.dynamicChildren&&(e.dynamicChildren=t.filter(r=>r!==e)),e}function Vv(e,t){t&&t.pendingBranch?ve(e)?t.effects.push(...e):t.effects.push(e):Ja(e)}function Un(e,t){e.activeBranch=t;const{vnode:r,parentComponent:n}=e;let i=t.el;for(;!i&&t.component;)t=t.component.subTree,i=t.el;r.el=i,n&&n.subTree===r&&(n.vnode.el=i,Ls(n,i))}function Y0(e){const t=e.props&&e.props.suspensible;return t!=null&&t!==!1}const lt=Symbol.for("v-fgt"),sn=Symbol.for("v-txt"),Ye=Symbol.for("v-cmt"),Ii=Symbol.for("v-stc"),ki=[];let Et=null;function bt(e=!1){ki.push(Et=e?null:[])}function $v(){ki.pop(),Et=ki[ki.length-1]||null}let $n=1;function cs(e,t=!1){$n+=e,e<0&&Et&&t&&(Et.hasOnce=!0)}function Wv(e){return e.dynamicChildren=$n>0?Et||Rn:null,$v(),$n>0&&Et&&Et.push(e),e}function Cn(e,t,r,n,i,o){return Wv(xr(e,t,r,n,i,o,!0))}function Qr(e,t,r,n,i){return Wv(De(e,t,r,n,i,!0))}function Wn(e){return e?e.__v_isVNode===!0:!1}function Bt(e,t){return e.type===t.type&&e.key===t.key}const Gv=({key:e})=>e??null,Uo=({ref:e,ref_key:t,ref_for:r})=>(typeof e=="number"&&(e=""+e),e!=null?Be(e)||Ve(e)||he(e)?{i:Ct,r:e,k:t,f:!!r}:e:null);function xr(e,t=null,r=null,n=0,i=null,o=e===lt?0:1,s=!1,a=!1){const u={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Gv(t),ref:t&&Uo(t),scopeId:av,slotScopeIds:null,children:r,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:n,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:Ct};return a?(Sl(u,r),o&128&&e.normalize(u)):r&&(u.shapeFlag|=Be(r)?8:16),$n>0&&!s&&Et&&(u.patchFlag>0||o&6)&&u.patchFlag!==32&&Et.push(u),u}const De=J0;function J0(e,t=null,r=null,n=0,i=null,o=!1){if((!e||e===Sv)&&(e=Ye),Wn(e)){const a=xt(e,t,!0);return r&&Sl(a,r),$n>0&&!o&&Et&&(a.shapeFlag&6?Et[Et.indexOf(e)]=a:Et.push(a)),a.patchFlag=-2,a}if(ub(e)&&(e=e.__vccOpts),t){t=Kv(t);let{class:a,style:u}=t;a&&!Be(a)&&(t.class=Ps(a)),Ue(u)&&(Rs(u)&&!ve(u)&&(u=Je({},u)),t.style=ks(u))}const s=Be(e)?1:ls(e)?128:lv(e)?64:Ue(e)?4:he(e)?2:0;return xr(e,t,r,n,i,s,o,!0)}function Kv(e){return e?Rs(e)||Rv(e)?Je({},e):e:null}function xt(e,t,r=!1,n=!1){const{props:i,ref:o,patchFlag:s,children:a,transition:u}=e,c=t?eb(i||{},t):i,l={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&Gv(c),ref:t&&t.ref?r&&o?ve(o)?o.concat(Uo(t)):[o,Uo(t)]:Uo(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:a,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==lt?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:u,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&xt(e.ssContent),ssFallback:e.ssFallback&&xt(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return u&&n&&qn(l,u.clone(l)),l}function Pi(e=" ",t=0){return De(sn,null,e,t)}function Q0(e,t){const r=De(Ii,null,e);return r.staticCount=t,r}function Z0(e="",t=!1){return t?(bt(),Qr(Ye,null,e)):De(Ye,null,e)}function At(e){return e==null||typeof e=="boolean"?De(Ye):ve(e)?De(lt,null,e.slice()):Wn(e)?Cr(e):De(sn,null,String(e))}function Cr(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:xt(e)}function Sl(e,t){let r=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(ve(t))r=16;else if(typeof t=="object")if(n&65){const i=t.default;i&&(i._c&&(i._d=!1),Sl(e,i()),i._c&&(i._d=!0));return}else{r=32;const i=t._;!i&&!Rv(t)?t._ctx=Ct:i===3&&Ct&&(Ct.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else he(t)?(t={default:t,_ctx:Ct},r=32):(t=String(t),n&64?(r=16,t=[Pi(t)]):r=8);e.children=t,e.shapeFlag|=r}function eb(...e){const t={};for(let r=0;rit||Ct;let fs,iu;{const e=Is(),t=(r,n)=>{let i;return(i=e[r])||(i=e[r]=[]),i.push(n),o=>{i.length>1?i.forEach(s=>s(o)):i[0](o)}};fs=t("__VUE_INSTANCE_SETTERS__",r=>it=r),iu=t("__VUE_SSR_SETTERS__",r=>Gn=r)}const to=e=>{const t=it;return fs(e),e.scope.on(),()=>{e.scope.off(),fs(t)}},Ac=()=>{it&&it.scope.off(),fs(null)};function zv(e){return e.vnode.shapeFlag&4}let Gn=!1;function ib(e,t=!1,r=!1){t&&iu(t);const{props:n,children:i}=e.vnode,o=zv(e);D0(e,n,o,t),B0(e,i,r||t);const s=o?ob(e,t):void 0;return t&&iu(!1),s}function ob(e,t){const r=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,_0);const{setup:n}=r;if(n){pr();const i=e.setupContext=n.length>1?ab(e):null,o=to(e),s=Zi(n,e,0,[e.props,i]),a=al(s);if(gr(),o(),(a||e.sp)&&!Ur(e)&&bl(e),a){if(s.then(Ac,Ac),t)return s.then(u=>{ou(e,u)}).catch(u=>{Zn(u,e,0)});e.asyncDep=s}else ou(e,s)}else Xv(e)}function ou(e,t,r){he(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Ue(t)&&(e.setupState=rv(t)),Xv(e)}function Xv(e,t,r){const n=e.type;e.render||(e.render=n.render||Jt);{const i=to(e);pr();try{S0(e)}finally{gr(),i()}}}const sb={get(e,t){return ut(e,"get",""),e[t]}};function ab(e){const t=r=>{e.exposed=r||{}};return{attrs:new Proxy(e.attrs,sb),slots:e.slots,emit:e.emit,expose:t}}function Ms(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(rv(By(e.exposed)),{get(t,r){if(r in t)return t[r];if(r in Ci)return Ci[r](e)},has(t,r){return r in t||r in Ci}})):e.proxy}function su(e,t=!0){return he(e)?e.displayName||e.name:e.name||t&&e.__name}function ub(e){return he(e)&&"__vccOpts"in e}const We=(e,t)=>zy(e,t,Gn);function Ke(e,t,r){try{cs(-1);const n=arguments.length;return n===2?Ue(t)&&!ve(t)?Wn(t)?De(e,null,[t]):De(e,t):De(e,null,t):(n>3?r=Array.prototype.slice.call(arguments,2):n===3&&Wn(r)&&(r=[r]),De(e,t,r))}finally{cs(1)}}const lb="3.5.27";let au;const Cc=typeof window<"u"&&window.trustedTypes;if(Cc)try{au=Cc.createPolicy("vue",{createHTML:e=>e})}catch{}const Yv=au?e=>au.createHTML(e):e=>e,cb="http://www.w3.org/2000/svg",fb="http://www.w3.org/1998/Math/MathML",ar=typeof document<"u"?document:null,Ic=ar&&ar.createElement("template"),db={insert:(e,t,r)=>{t.insertBefore(e,r||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,r,n)=>{const i=t==="svg"?ar.createElementNS(cb,e):t==="mathml"?ar.createElementNS(fb,e):r?ar.createElement(e,{is:r}):ar.createElement(e);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>ar.createTextNode(e),createComment:e=>ar.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>ar.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,r,n,i,o){const s=r?r.previousSibling:t.lastChild;if(i&&(i===o||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),r),!(i===o||!(i=i.nextSibling)););else{Ic.innerHTML=Yv(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const a=Ic.content;if(n==="svg"||n==="mathml"){const u=a.firstChild;for(;u.firstChild;)a.appendChild(u.firstChild);a.removeChild(u)}t.insertBefore(a,r)}return[s?s.nextSibling:t.firstChild,r?r.previousSibling:t.lastChild]}},_r="transition",si="animation",Gi=Symbol("_vtc"),Jv={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},hb=Je({},cv,Jv),vb=e=>(e.displayName="Transition",e.props=hb,e),pb=vb((e,{slots:t})=>Ke(s0,gb(e),t)),$r=(e,t=[])=>{ve(e)?e.forEach(r=>r(...t)):e&&e(...t)},kc=e=>e?ve(e)?e.some(t=>t.length>1):e.length>1:!1;function gb(e){const t={};for(const k in e)k in Jv||(t[k]=e[k]);if(e.css===!1)return t;const{name:r="v",type:n,duration:i,enterFromClass:o=`${r}-enter-from`,enterActiveClass:s=`${r}-enter-active`,enterToClass:a=`${r}-enter-to`,appearFromClass:u=o,appearActiveClass:c=s,appearToClass:l=a,leaveFromClass:f=`${r}-leave-from`,leaveActiveClass:d=`${r}-leave-active`,leaveToClass:h=`${r}-leave-to`}=e,v=mb(i),p=v&&v[0],b=v&&v[1],{onBeforeEnter:E,onEnter:m,onEnterCancelled:g,onLeave:y,onLeaveCancelled:_,onBeforeAppear:S=E,onAppear:A=m,onAppearCancelled:C=g}=t,I=(k,N,$,ee)=>{k._enterCancelled=ee,Wr(k,N?l:a),Wr(k,N?c:s),$&&$()},P=(k,N)=>{k._isLeaving=!1,Wr(k,f),Wr(k,h),Wr(k,d),N&&N()},R=k=>(N,$)=>{const ee=k?A:m,B=()=>I(N,k,$);$r(ee,[N,B]),Pc(()=>{Wr(N,k?u:o),ir(N,k?l:a),kc(ee)||Rc(N,n,p,B)})};return Je(t,{onBeforeEnter(k){$r(E,[k]),ir(k,o),ir(k,s)},onBeforeAppear(k){$r(S,[k]),ir(k,u),ir(k,c)},onEnter:R(!1),onAppear:R(!0),onLeave(k,N){k._isLeaving=!0;const $=()=>P(k,N);ir(k,f),k._enterCancelled?(ir(k,d),Nc(k)):(Nc(k),ir(k,d)),Pc(()=>{k._isLeaving&&(Wr(k,f),ir(k,h),kc(y)||Rc(k,n,b,$))}),$r(y,[k,$])},onEnterCancelled(k){I(k,!1,void 0,!0),$r(g,[k])},onAppearCancelled(k){I(k,!0,void 0,!0),$r(C,[k])},onLeaveCancelled(k){P(k),$r(_,[k])}})}function mb(e){if(e==null)return null;if(Ue(e))return[sa(e.enter),sa(e.leave)];{const t=sa(e);return[t,t]}}function sa(e){return Mh(e)}function ir(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.add(r)),(e[Gi]||(e[Gi]=new Set)).add(t)}function Wr(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const r=e[Gi];r&&(r.delete(t),r.size||(e[Gi]=void 0))}function Pc(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let yb=0;function Rc(e,t,r,n){const i=e._endId=++yb,o=()=>{i===e._endId&&n()};if(r!=null)return setTimeout(o,r);const{type:s,timeout:a,propCount:u}=bb(e,t);if(!s)return n();const c=s+"end";let l=0;const f=()=>{e.removeEventListener(c,d),o()},d=h=>{h.target===e&&++l>=u&&f()};setTimeout(()=>{l(r[v]||"").split(", "),i=n(`${_r}Delay`),o=n(`${_r}Duration`),s=Oc(i,o),a=n(`${si}Delay`),u=n(`${si}Duration`),c=Oc(a,u);let l=null,f=0,d=0;t===_r?s>0&&(l=_r,f=s,d=o.length):t===si?c>0&&(l=si,f=c,d=u.length):(f=Math.max(s,c),l=f>0?s>c?_r:si:null,d=l?l===_r?o.length:u.length:0);const h=l===_r&&/\b(?:transform|all)(?:,|$)/.test(n(`${_r}Property`).toString());return{type:l,timeout:f,propCount:d,hasTransform:h}}function Oc(e,t){for(;e.lengthxc(r)+xc(e[n])))}function xc(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Nc(e){return(e?e.ownerDocument:document).body.offsetHeight}function Eb(e,t,r){const n=e[Gi];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):r?e.setAttribute("class",t):e.className=t}const Lc=Symbol("_vod"),_b=Symbol("_vsh"),Sb=Symbol(""),wb=/(?:^|;)\s*display\s*:/;function Tb(e,t,r){const n=e.style,i=Be(r);let o=!1;if(r&&!i){if(t)if(Be(t))for(const s of t.split(";")){const a=s.slice(0,s.indexOf(":")).trim();r[a]==null&&Fo(n,a,"")}else for(const s in t)r[s]==null&&Fo(n,s,"");for(const s in r)s==="display"&&(o=!0),Fo(n,s,r[s])}else if(i){if(t!==r){const s=n[Sb];s&&(r+=";"+s),n.cssText=r,o=wb.test(r)}}else t&&e.removeAttribute("style");Lc in e&&(e[Lc]=o?n.display:"",e[_b]&&(n.display="none"))}const Mc=/\s*!important$/;function Fo(e,t,r){if(ve(r))r.forEach(n=>Fo(e,t,n));else if(r==null&&(r=""),t.startsWith("--"))e.setProperty(t,r);else{const n=Ab(e,t);Mc.test(r)?e.setProperty(dn(n),r.replace(Mc,""),"important"):e[n]=r}}const Dc=["Webkit","Moz","ms"],aa={};function Ab(e,t){const r=aa[t];if(r)return r;let n=Mt(t);if(n!=="filter"&&n in e)return aa[t]=n;n=Cs(n);for(let i=0;iua||(Pb.then(()=>ua=0),ua=Date.now());function Ob(e,t){const r=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=r.attached)return;qt(xb(n,r.value),t,5,[n])};return r.value=e,r.attached=Rb(),r}function xb(e,t){if(ve(t)){const r=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{r.call(e),e._stopped=!0},t.map(n=>i=>!i._stopped&&n&&n(i))}else return t}const qc=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Nb=(e,t,r,n,i,o)=>{const s=i==="svg";t==="class"?Eb(e,n,s):t==="style"?Tb(e,r,n):Ji(t)?ol(t)||Ib(e,t,r,n,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Lb(e,t,n,s))?(Hc(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Fc(e,t,n,s,o,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!Be(n))?Hc(e,Mt(t),n,o,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Fc(e,t,n,s))};function Lb(e,t,r,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&qc(t)&&he(r));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return qc(t)&&Be(r)?!1:t in e}const Vc=e=>{const t=e.props["onUpdate:modelValue"]||!1;return ve(t)?r=>xn(t,r):t};function Mb(e){e.target.composing=!0}function $c(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const la=Symbol("_assign");function Wc(e,t,r){return t&&(e=e.trim()),r&&(e=ul(e)),e}const yN={created(e,{modifiers:{lazy:t,trim:r,number:n}},i){e[la]=Vc(i);const o=n||i.props&&i.props.type==="number";Sn(e,t?"change":"input",s=>{s.target.composing||e[la](Wc(e.value,r,o))}),(r||o)&&Sn(e,"change",()=>{e.value=Wc(e.value,r,o)}),t||(Sn(e,"compositionstart",Mb),Sn(e,"compositionend",$c),Sn(e,"change",$c))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:r,modifiers:{lazy:n,trim:i,number:o}},s){if(e[la]=Vc(s),e.composing)return;const a=(o||e.type==="number")&&!/^0\d/.test(e.value)?ul(e.value):e.value,u=t??"";a!==u&&(document.activeElement===e&&e.type!=="range"&&(n&&t===r||i&&e.value.trim()===u)||(e.value=u))}},Db=["ctrl","shift","alt","meta"],Ub={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Db.some(r=>e[`${r}Key`]&&!t.includes(r))},bN=(e,t)=>{const r=e._withMods||(e._withMods={}),n=t.join(".");return r[n]||(r[n]=((i,...o)=>{for(let s=0;s{const t=Fb().createApp(...e),{mount:r}=t;return t.mount=n=>{const i=ep(n);if(!i)return;const o=t._component;!he(o)&&!o.render&&!o.template&&(o.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const s=r(i,!1,Zv(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),s},t}),jb=((...e)=>{const t=Hb().createApp(...e),{mount:r}=t;return t.mount=n=>{const i=ep(n);if(i)return r(i,!0,Zv(i))},t});function Zv(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function ep(e){return Be(e)?document.querySelector(e):e}const qb=/"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/,Vb=/"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/,$b=/^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;function Wb(e,t){if(e==="__proto__"||e==="constructor"&&t&&typeof t=="object"&&"prototype"in t){Gb(e);return}return t}function Gb(e){console.warn(`[destr] Dropping "${e}" key to prevent prototype pollution.`)}function ds(e,t={}){if(typeof e!="string")return e;if(e[0]==='"'&&e[e.length-1]==='"'&&e.indexOf("\\")===-1)return e.slice(1,-1);const r=e.trim();if(r.length<=9)switch(r.toLowerCase()){case"true":return!0;case"false":return!1;case"undefined":return;case"null":return null;case"nan":return Number.NaN;case"infinity":return Number.POSITIVE_INFINITY;case"-infinity":return Number.NEGATIVE_INFINITY}if(!$b.test(e)){if(t.strict)throw new SyntaxError("[destr] Invalid JSON");return e}try{if(qb.test(e)||Vb.test(e)){if(t.strict)throw new Error("[destr] Possible prototype pollution");return JSON.parse(e,Wb)}return JSON.parse(e)}catch(n){if(t.strict)throw n;return e}}const Kb=/#/g,zb=/&/g,Xb=/\//g,Yb=/=/g,wl=/\+/g,Jb=/%5e/gi,Qb=/%60/gi,Zb=/%7c/gi,eE=/%20/gi,tE=/%2f/gi;function rE(e){return encodeURI(""+e).replace(Zb,"|")}function uu(e){return rE(typeof e=="string"?e:JSON.stringify(e)).replace(wl,"%2B").replace(eE,"+").replace(Kb,"%23").replace(zb,"%26").replace(Qb,"`").replace(Jb,"^").replace(Xb,"%2F")}function ca(e){return uu(e).replace(Yb,"%3D")}function Ki(e=""){try{return decodeURIComponent(""+e)}catch{return""+e}}function Kc(e){return Ki(e.replace(tE,"%252F"))}function nE(e){return Ki(e.replace(wl," "))}function iE(e){return Ki(e.replace(wl," "))}function Tl(e=""){const t=Object.create(null);e[0]==="?"&&(e=e.slice(1));for(const r of e.split("&")){const n=r.match(/([^=]+)=?(.*)/)||[];if(n.length<2)continue;const i=nE(n[1]);if(i==="__proto__"||i==="constructor")continue;const o=iE(n[2]||"");t[i]===void 0?t[i]=o:Array.isArray(t[i])?t[i].push(o):t[i]=[t[i],o]}return t}function oE(e,t){return(typeof t=="number"||typeof t=="boolean")&&(t=String(t)),t?Array.isArray(t)?t.map(r=>`${ca(e)}=${uu(r)}`).join("&"):`${ca(e)}=${uu(t)}`:ca(e)}function sE(e){return Object.keys(e).filter(t=>e[t]!==void 0).map(t=>oE(t,e[t])).filter(Boolean).join("&")}const aE=/^[\s\w\0+.-]{2,}:([/\\]{1,2})/,uE=/^[\s\w\0+.-]{2,}:([/\\]{2})?/,lE=/^([/\\]\s*){2,}[^/\\]/,cE=/^[\s\0]*(blob|data|javascript|vbscript):$/i,fE=/\/$|\/\?|\/#/,dE=/^\.?\//;function qr(e,t={}){return typeof t=="boolean"&&(t={acceptRelative:t}),t.strict?aE.test(e):uE.test(e)||(t.acceptRelative?lE.test(e):!1)}function hE(e){return!!e&&cE.test(e)}function lu(e="",t){return t?fE.test(e):e.endsWith("/")}function zi(e="",t){if(!t)return(lu(e)?e.slice(0,-1):e)||"/";if(!lu(e,!0))return e||"/";let r=e,n="";const i=e.indexOf("#");i!==-1&&(r=e.slice(0,i),n=e.slice(i));const[o,...s]=r.split("?");return((o.endsWith("/")?o.slice(0,-1):o)||"/")+(s.length>0?`?${s.join("?")}`:"")+n}function tp(e="",t){if(!t)return e.endsWith("/")?e:e+"/";if(lu(e,!0))return e||"/";let r=e,n="";const i=e.indexOf("#");if(i!==-1&&(r=e.slice(0,i),n=e.slice(i),!r))return n;const[o,...s]=r.split("?");return o+"/"+(s.length>0?`?${s.join("?")}`:"")+n}function vE(e,t){if(np(t)||qr(e))return e;const r=zi(t);if(e.startsWith(r)){const n=e[r.length];if(!n||n==="/"||n==="?")return e}return Al(r,e)}function zc(e,t){if(np(t))return e;const r=zi(t);if(!e.startsWith(r))return e;const n=e[r.length];if(n&&n!=="/"&&n!=="?")return e;const i=e.slice(r.length);return i[0]==="/"?i:"/"+i}function rp(e,t){const r=sp(e),n={...Tl(r.search),...t};return r.search=sE(n),mE(r)}function np(e){return!e||e==="/"}function pE(e){return e&&e!=="/"}function Al(e,...t){let r=e||"";for(const n of t.filter(i=>pE(i)))if(r){const i=n.replace(dE,"");r=tp(r)+i}else r=n;return r}function ip(...e){const t=/\/(?!\/)/,r=e.filter(Boolean),n=[];let i=0;for(const s of r)if(!(!s||s==="/")){for(const[a,u]of s.split(t).entries())if(!(!u||u===".")){if(u===".."){if(n.length===1&&qr(n[0]))continue;n.pop(),i--;continue}if(a===1&&n[n.length-1]?.endsWith(":/")){n[n.length-1]+="/"+u;continue}n.push(u),i++}}let o=n.join("/");return i>=0?r[0]?.startsWith("/")&&!o.startsWith("/")?o="/"+o:r[0]?.startsWith("./")&&!o.startsWith("./")&&(o="./"+o):o="../".repeat(-1*i)+o,r[r.length-1]?.endsWith("/")&&!o.endsWith("/")&&(o+="/"),o}function gE(e,t){return Ki(zi(e))===Ki(zi(t))}const op=Symbol.for("ufo:protocolRelative");function sp(e="",t){const r=e.match(/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i);if(r){const[,f,d=""]=r;return{protocol:f.toLowerCase(),pathname:d,href:f+d,auth:"",host:"",search:"",hash:""}}if(!qr(e,{acceptRelative:!0}))return Xc(e);const[,n="",i,o=""]=e.replace(/\\/g,"/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/)||[];let[,s="",a=""]=o.match(/([^#/?]*)(.*)?/)||[];n==="file:"&&(a=a.replace(/\/(?=[A-Za-z]:)/,""));const{pathname:u,search:c,hash:l}=Xc(a);return{protocol:n.toLowerCase(),auth:i?i.slice(0,Math.max(0,i.length-1)):"",host:s,pathname:u,search:c,hash:l,[op]:!n}}function Xc(e=""){const[t="",r="",n=""]=(e.match(/([^#?]*)(\?[^#]*)?(#.*)?/)||[]).splice(1);return{pathname:t,search:r,hash:n}}function mE(e){const t=e.pathname||"",r=e.search?(e.search.startsWith("?")?"":"?")+e.search:"",n=e.hash||"",i=e.auth?e.auth+"@":"",o=e.host||"";return(e.protocol||e[op]?(e.protocol||"")+"//":"")+i+o+t+r+n}let yE=class extends Error{constructor(t,r){super(t,r),this.name="FetchError",r?.cause&&!this.cause&&(this.cause=r.cause)}};function bE(e){const t=e.error?.message||e.error?.toString()||"",r=e.request?.method||e.options?.method||"GET",n=e.request?.url||String(e.request)||"/",i=`[${r}] ${JSON.stringify(n)}`,o=e.response?`${e.response.status} ${e.response.statusText}`:"",s=`${i}: ${o}${t?` ${t}`:""}`,a=new yE(s,e.error?{cause:e.error}:void 0);for(const u of["request","options","response"])Object.defineProperty(a,u,{get(){return e[u]}});for(const[u,c]of[["data","_data"],["status","status"],["statusCode","status"],["statusText","statusText"],["statusMessage","statusText"]])Object.defineProperty(a,u,{get(){return e.response&&e.response[c]}});return a}const EE=new Set(Object.freeze(["PATCH","POST","PUT","DELETE"]));function Yc(e="GET"){return EE.has(e.toUpperCase())}function _E(e){if(e===void 0)return!1;const t=typeof e;return t==="string"||t==="number"||t==="boolean"||t===null?!0:t!=="object"?!1:Array.isArray(e)?!0:e.buffer||e instanceof FormData||e instanceof URLSearchParams?!1:e.constructor&&e.constructor.name==="Object"||typeof e.toJSON=="function"}const SE=new Set(["image/svg","application/xml","application/xhtml","application/html"]),wE=/^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;function TE(e=""){if(!e)return"json";const t=e.split(";").shift()||"";return wE.test(t)?"json":t==="text/event-stream"?"stream":SE.has(t)||t.startsWith("text/")?"text":"blob"}function AE(e,t,r,n){const i=CE(t?.headers??e?.headers,r?.headers,n);let o;return(r?.query||r?.params||t?.params||t?.query)&&(o={...r?.params,...r?.query,...t?.params,...t?.query}),{...r,...t,query:o,params:o,headers:i}}function CE(e,t,r){if(!t)return new r(e);const n=new r(t);if(e)for(const[i,o]of Symbol.iterator in e||Array.isArray(e)?e:new r(e))n.set(i,o);return n}async function go(e,t){if(t)if(Array.isArray(t))for(const r of t)await r(e);else await t(e)}const IE=new Set([408,409,425,429,500,502,503,504]),kE=new Set([101,204,205,304]);function ap(e={}){const{fetch:t=globalThis.fetch,Headers:r=globalThis.Headers,AbortController:n=globalThis.AbortController}=e;async function i(a){const u=a.error&&a.error.name==="AbortError"&&!a.options.timeout||!1;if(a.options.retry!==!1&&!u){let l;typeof a.options.retry=="number"?l=a.options.retry:l=Yc(a.options.method)?0:1;const f=a.response&&a.response.status||500;if(l>0&&(Array.isArray(a.options.retryStatusCodes)?a.options.retryStatusCodes.includes(f):IE.has(f))){const d=typeof a.options.retryDelay=="function"?a.options.retryDelay(a):a.options.retryDelay||0;return d>0&&await new Promise(h=>setTimeout(h,d)),o(a.request,{...a.options,retry:l-1})}}const c=bE(a);throw Error.captureStackTrace&&Error.captureStackTrace(c,o),c}const o=async function(u,c={}){const l={request:u,options:AE(u,c,e.defaults,r),response:void 0,error:void 0};if(l.options.method&&(l.options.method=l.options.method.toUpperCase()),l.options.onRequest&&(await go(l,l.options.onRequest),l.options.headers instanceof r||(l.options.headers=new r(l.options.headers||{}))),typeof l.request=="string"&&(l.options.baseURL&&(l.request=vE(l.request,l.options.baseURL)),l.options.query&&(l.request=rp(l.request,l.options.query),delete l.options.query),"query"in l.options&&delete l.options.query,"params"in l.options&&delete l.options.params),l.options.body&&Yc(l.options.method))if(_E(l.options.body)){const h=l.options.headers.get("content-type");typeof l.options.body!="string"&&(l.options.body=h==="application/x-www-form-urlencoded"?new URLSearchParams(l.options.body).toString():JSON.stringify(l.options.body)),h||l.options.headers.set("content-type","application/json"),l.options.headers.has("accept")||l.options.headers.set("accept","application/json")}else("pipeTo"in l.options.body&&typeof l.options.body.pipeTo=="function"||typeof l.options.body.pipe=="function")&&("duplex"in l.options||(l.options.duplex="half"));let f;if(!l.options.signal&&l.options.timeout){const h=new n;f=setTimeout(()=>{const v=new Error("[TimeoutError]: The operation was aborted due to timeout");v.name="TimeoutError",v.code=23,h.abort(v)},l.options.timeout),l.options.signal=h.signal}try{l.response=await t(l.request,l.options)}catch(h){return l.error=h,l.options.onRequestError&&await go(l,l.options.onRequestError),await i(l)}finally{f&&clearTimeout(f)}if((l.response.body||l.response._bodyInit)&&!kE.has(l.response.status)&&l.options.method!=="HEAD"){const h=(l.options.parseResponse?"json":l.options.responseType)||TE(l.response.headers.get("content-type")||"");switch(h){case"json":{const v=await l.response.text(),p=l.options.parseResponse||ds;l.response._data=p(v);break}case"stream":{l.response._data=l.response.body||l.response._bodyInit;break}default:l.response._data=await l.response[h]()}}return l.options.onResponse&&await go(l,l.options.onResponse),!l.options.ignoreResponseError&&l.response.status>=400&&l.response.status<600?(l.options.onResponseError&&await go(l,l.options.onResponseError),await i(l)):l.response},s=async function(u,c){return(await o(u,c))._data};return s.raw=o,s.native=(...a)=>t(...a),s.create=(a={},u={})=>ap({...e,...u,defaults:{...e.defaults,...u.defaults,...a}}),s}const hs=(function(){if(typeof globalThis<"u")return globalThis;if(typeof self<"u")return self;if(typeof window<"u")return window;if(typeof global<"u")return global;throw new Error("unable to locate global object")})(),PE=hs.fetch?(...e)=>hs.fetch(...e):()=>Promise.reject(new Error("[ofetch] global.fetch is not supported!")),RE=hs.Headers,OE=hs.AbortController,xE=ap({fetch:PE,Headers:RE,AbortController:OE}),NE=xE,LE=()=>window?.__NUXT__?.config||{},Cl=()=>LE().app,ME=()=>Cl().baseURL,DE=()=>Cl().buildAssetsDir,Il=(...e)=>ip(up(),DE(),...e),up=(...e)=>{const t=Cl(),r=t.cdnURL||t.baseURL;return e.length?ip(r,...e):r};globalThis.__buildAssetsURL=Il,globalThis.__publicAssetsURL=up;globalThis.$fetch||(globalThis.$fetch=NE.create({baseURL:ME()}));"global"in globalThis||(globalThis.global=globalThis);function cu(e,t={},r){for(const n in e){const i=e[n],o=r?`${r}:${n}`:n;typeof i=="object"&&i!==null?cu(i,t,o):typeof i=="function"&&(t[o]=i)}return t}const UE={run:e=>e()},FE=()=>UE,lp=typeof console.createTask<"u"?console.createTask:FE;function HE(e,t){const r=t.shift(),n=lp(r);return e.reduce((i,o)=>i.then(()=>n.run(()=>o(...t))),Promise.resolve())}function BE(e,t){const r=t.shift(),n=lp(r);return Promise.all(e.map(i=>n.run(()=>i(...t))))}function fa(e,t){for(const r of[...e])r(t)}let jE=class{constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(t,r,n={}){if(!t||typeof r!="function")return()=>{};const i=t;let o;for(;this._deprecatedHooks[t];)o=this._deprecatedHooks[t],t=o.to;if(o&&!n.allowDeprecated){let s=o.message;s||(s=`${i} hook has been deprecated`+(o.to?`, please use ${o.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(s)||(console.warn(s),this._deprecatedMessages.add(s))}if(!r.name)try{Object.defineProperty(r,"name",{get:()=>"_"+t.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[t]=this._hooks[t]||[],this._hooks[t].push(r),()=>{r&&(this.removeHook(t,r),r=void 0)}}hookOnce(t,r){let n,i=(...o)=>(typeof n=="function"&&n(),n=void 0,i=void 0,r(...o));return n=this.hook(t,i),n}removeHook(t,r){if(this._hooks[t]){const n=this._hooks[t].indexOf(r);n!==-1&&this._hooks[t].splice(n,1),this._hooks[t].length===0&&delete this._hooks[t]}}deprecateHook(t,r){this._deprecatedHooks[t]=typeof r=="string"?{to:r}:r;const n=this._hooks[t]||[];delete this._hooks[t];for(const i of n)this.hook(t,i)}deprecateHooks(t){Object.assign(this._deprecatedHooks,t);for(const r in t)this.deprecateHook(r,t[r])}addHooks(t){const r=cu(t),n=Object.keys(r).map(i=>this.hook(i,r[i]));return()=>{for(const i of n.splice(0,n.length))i()}}removeHooks(t){const r=cu(t);for(const n in r)this.removeHook(n,r[n])}removeAllHooks(){for(const t in this._hooks)delete this._hooks[t]}callHook(t,...r){return r.unshift(t),this.callHookWith(HE,t,...r)}callHookParallel(t,...r){return r.unshift(t),this.callHookWith(BE,t,...r)}callHookWith(t,r,...n){const i=this._before||this._after?{name:r,args:n,context:{}}:void 0;this._before&&fa(this._before,i);const o=t(r in this._hooks?[...this._hooks[r]]:[],n);return o instanceof Promise?o.finally(()=>{this._after&&i&&fa(this._after,i)}):(this._after&&i&&fa(this._after,i),o)}beforeEach(t){return this._before=this._before||[],this._before.push(t),()=>{if(this._before!==void 0){const r=this._before.indexOf(t);r!==-1&&this._before.splice(r,1)}}}afterEach(t){return this._after=this._after||[],this._after.push(t),()=>{if(this._after!==void 0){const r=this._after.indexOf(t);r!==-1&&this._after.splice(r,1)}}}};function qE(){return new jE}function VE(e={}){let t,r=!1;const n=s=>{if(t&&t!==s)throw new Error("Context conflict")};let i;if(e.asyncContext){const s=e.AsyncLocalStorage||globalThis.AsyncLocalStorage;s?i=new s:console.warn("[unctx] `AsyncLocalStorage` is not provided.")}const o=()=>{if(i){const s=i.getStore();if(s!==void 0)return s}return t};return{use:()=>{const s=o();if(s===void 0)throw new Error("Context is not available");return s},tryUse:()=>o(),set:(s,a)=>{a||n(s),t=s,r=!0},unset:()=>{t=void 0,r=!1},call:(s,a)=>{n(s),t=s;try{return i?i.run(s,a):a()}finally{r||(t=void 0)}},async callAsync(s,a){t=s;const u=()=>{t=s},c=()=>t===s?u:void 0;fu.add(c);try{const l=i?i.run(s,a):a();return r||(t=void 0),await l}finally{fu.delete(c)}}}}function $E(e={}){const t={};return{get(r,n={}){return t[r]||(t[r]=VE({...e,...n})),t[r]}}}const vs=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof global<"u"?global:typeof window<"u"?window:{},Jc="__unctx__",WE=vs[Jc]||(vs[Jc]=$E()),GE=(e,t={})=>WE.get(e,t),Qc="__unctx_async_handlers__",fu=vs[Qc]||(vs[Qc]=new Set);function Oi(e){const t=[];for(const i of fu){const o=i();o&&t.push(o)}const r=()=>{for(const i of t)i()};let n=e();return n&&typeof n=="object"&&"catch"in n&&(n=n.catch(i=>{throw r(),i})),[n,r]}const Zc=!1,KE=!1,zE={componentName:"NuxtLink",prefetch:!0,prefetchOn:{visibility:!0}},XE=null,YE="#__nuxt",cp="nuxt-app",ef=36e5,JE="vite:preloadError";function fp(e=cp){return GE(e,{asyncContext:!1})}const QE="__nuxt_plugin";function ZE(e){let t=0;const r={_id:e.id||cp||"nuxt-app",_scope:by(),provide:void 0,globalName:"nuxt",versions:{get nuxt(){return"3.21.0"},get vue(){return r.vueApp.version}},payload:hr({...e.ssrContext?.payload||{},data:hr({}),state:Br({}),once:new Set,_errors:hr({})}),static:{data:{}},runWithContext(i){return r._scope.active&&!ll()?r._scope.run(()=>tf(r,i)):tf(r,i)},isHydrating:!0,deferHydration(){if(!r.isHydrating)return()=>{};t++;let i=!1;return()=>{if(!i&&(i=!0,t--,t===0))return r.isHydrating=!1,r.callHook("app:suspense:resolve")}},_asyncDataPromises:{},_asyncData:hr({}),_payloadRevivers:{},...e};{const i=window.__NUXT__;if(i)for(const o in i)switch(o){case"data":case"state":case"_errors":Object.assign(r.payload[o],i[o]);break;default:r.payload[o]=i[o]}}r.hooks=qE(),r.hook=r.hooks.hook,r.callHook=r.hooks.callHook,r.provide=(i,o)=>{const s="$"+i;mo(r,s,o),mo(r.vueApp.config.globalProperties,s,o)},mo(r.vueApp,"$nuxt",r),mo(r.vueApp.config.globalProperties,"$nuxt",r);{window.addEventListener(JE,o=>{r.callHook("app:chunkError",{error:o.payload}),o.payload.message.includes("Unable to preload CSS")&&o.preventDefault()}),window.useNuxtApp||=He;const i=r.hook("app:error",(...o)=>{console.error("[nuxt] error caught during app initialization",...o)});r.hook("app:mounted",i)}const n=r.payload.config;return r.provide("config",n),r}function e_(e,t){t.hooks&&e.hooks.addHooks(t.hooks)}async function t_(e,t){if(typeof t=="function"){const{provide:r}=await e.runWithContext(()=>t(e))||{};if(r&&typeof r=="object")for(const n in r)e.provide(n,r[n])}}async function r_(e,t){const r=new Set,n=[],i=[];let o,s=0;async function a(u){const c=u.dependsOn?.filter(l=>t.some(f=>f._name===l)&&!r.has(l))??[];if(c.length>0)n.push([new Set(c),u]);else{const l=t_(e,u).then(async()=>{u._name&&(r.add(u._name),await Promise.all(n.map(async([f,d])=>{f.has(u._name)&&(f.delete(u._name),f.size===0&&(s++,await a(d)))})))}).catch(f=>{if(!u.parallel&&!e.payload.error)throw f;o||=f});u.parallel?i.push(l):await l}}for(const u of t)e_(e,u);for(const u of t)await a(u);if(await Promise.all(i),s)for(let u=0;u{}),e,{[QE]:!0,_name:t})}function tf(e,t,r){const n=()=>t();return fp(e._id).set(e),e.vueApp.runWithContext(n)}function dp(e){let t;return Os()&&(t=hn()?.appContext.app.$nuxt),t||=fp(e).tryUse(),t||null}function He(e){const t=dp(e);if(!t)throw new Error("[nuxt] instance unavailable");return t}function ro(e){return He().$config}function mo(e,t,r){Object.defineProperty(e,t,{get:()=>r})}function da(e){if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t!==null&&t!==Object.prototype&&Object.getPrototypeOf(t)!==null||Symbol.iterator in e?!1:Symbol.toStringTag in e?Object.prototype.toString.call(e)==="[object Module]":!0}function du(e,t,r=".",n){if(!da(t))return du(e,{},r,n);const i=Object.assign({},t);for(const o in e){if(o==="__proto__"||o==="constructor")continue;const s=e[o];s!=null&&(n&&n(i,o,s,r)||(Array.isArray(s)&&Array.isArray(i[o])?i[o]=[...s,...i[o]]:da(s)&&da(i[o])?i[o]=du(s,i[o],(r?`${r}.`:"")+o.toString(),n):i[o]=s))}return i}function n_(e){return(...t)=>t.reduce((r,n)=>du(r,n,"",e),{})}const hp=n_();function i_(e,t){try{return t in e}catch{return!1}}class rf extends Error{static __h3_error__=!0;statusCode=500;fatal=!1;unhandled=!1;statusMessage;data;cause;constructor(t,r={}){super(t,r),r.cause&&!this.cause&&(this.cause=r.cause)}toJSON(){const t={message:this.message,statusCode:hu(this.statusCode,500)};return this.statusMessage&&(t.statusMessage=vp(this.statusMessage)),this.data!==void 0&&(t.data=this.data),t}}function o_(e){if(typeof e=="string")return new rf(e);if(s_(e))return e;const t=new rf(e.message??e.statusMessage??"",{cause:e.cause||e});if(i_(e,"stack"))try{Object.defineProperty(t,"stack",{get(){return e.stack}})}catch{try{t.stack=e.stack}catch{}}if(e.data&&(t.data=e.data),e.statusCode?t.statusCode=hu(e.statusCode,t.statusCode):e.status&&(t.statusCode=hu(e.status,t.statusCode)),e.statusMessage?t.statusMessage=e.statusMessage:e.statusText&&(t.statusMessage=e.statusText),t.statusMessage){const r=t.statusMessage;vp(t.statusMessage)!==r&&console.warn("[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default.")}return e.fatal!==void 0&&(t.fatal=e.fatal),e.unhandled!==void 0&&(t.unhandled=e.unhandled),t}function s_(e){return e?.constructor?.__h3_error__===!0}const a_=/[^\u0009\u0020-\u007E]/g;function vp(e=""){return e.replace(a_,"")}function hu(e,t=200){return!e||(typeof e=="string"&&(e=Number.parseInt(e,10)),e<100||e>999)?t:e}const u_=Symbol("layout-meta"),Ds=Symbol("route");import.meta.url.replace(/\/app\/.*$/,"/");const wt=()=>He()?.$router,l_=()=>Os()?_t(Ds,He()._route):He()._route;const c_=()=>{try{if(He()._processingMiddleware)return!0}catch{return!1}return!1},f_=(e,t)=>{e||="/";const r=typeof e=="string"?e:"path"in e?vu(e):wt().resolve(e).href;if(t?.open){const{target:u="_blank",windowFeatures:c={}}=t.open,l=[];for(const[f,d]of Object.entries(c))d!==void 0&&l.push(`${f.toLowerCase()}=${d}`);return open(r,u,l.join(", ")),Promise.resolve()}const n=qr(r,{acceptRelative:!0}),i=t?.external||n;if(i){if(!t?.external)throw new Error("Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.");const{protocol:u}=new URL(r,window.location.href);if(u&&hE(u))throw new Error(`Cannot navigate to a URL with '${u}' protocol.`)}const o=c_();if(!i&&o){if(t?.replace){if(typeof e=="string"){const{pathname:u,search:c,hash:l}=sp(e);return{path:u,...c&&{query:Tl(c)},...l&&{hash:l},replace:!0}}return{...e,replace:!0}}return e}const s=wt(),a=He();return i?(a._scope.stop(),t?.replace?location.replace(r):location.href=r,o?a.isHydrating?new Promise(()=>{}):!1:Promise.resolve()):t?.replace?s.replace(e):s.push(e)};function vu(e){return rp(e.path||"",e.query||{})+(e.hash||"")}const pp="__nuxt_error",Us=()=>nv(He().payload,"error"),Yr=e=>{const t=an(e);try{const r=Us();He().hooks.callHook("app:error",t),r.value||=t}catch{throw t}return t},d_=async(e={})=>{const t=He(),r=Us();t.callHook("app:error:cleared",e),e.redirect&&await wt().replace(e.redirect),r.value=XE},gp=e=>!!e&&typeof e=="object"&&pp in e,an=e=>{typeof e!="string"&&e.statusText&&(e.message??=e.statusText);const t=o_(e);return Object.defineProperty(t,pp,{value:!0,configurable:!1,writable:!1}),t};function h_(e){const t=p_(e),r=new ArrayBuffer(t.length),n=new DataView(r);for(let i=0;i>16),t+=String.fromCharCode((r&65280)>>8),t+=String.fromCharCode(r&255),r=n=0);return n===12?(r>>=4,t+=String.fromCharCode(r)):n===18&&(r>>=2,t+=String.fromCharCode((r&65280)>>8),t+=String.fromCharCode(r&255)),t}const g_=-1,m_=-2,y_=-3,b_=-4,E_=-5,__=-6;function S_(e,t){return w_(JSON.parse(e),t)}function w_(e,t){if(typeof e=="number")return o(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const r=e,n=Array(r.length);let i=null;function o(s,a=!1){if(s===g_)return;if(s===y_)return NaN;if(s===b_)return 1/0;if(s===E_)return-1/0;if(s===__)return-0;if(a||typeof s!="number")throw new Error("Invalid input");if(s in n)return n[s];const u=r[s];if(!u||typeof u!="object")n[s]=u;else if(Array.isArray(u))if(typeof u[0]=="string"){const c=u[0],l=t&&Object.hasOwn(t,c)?t[c]:void 0;if(l){let f=u[1];if(typeof f!="number"&&(f=r.push(u[1])-1),i??=new Set,i.has(f))throw new Error("Invalid circular reference");return i.add(f),n[s]=l(o(f)),i.delete(f),n[s]}switch(c){case"Date":n[s]=new Date(u[1]);break;case"Set":const f=new Set;n[s]=f;for(let v=1;v{if(console.createTask)return console.createTask;const e={run:t=>t()};return()=>e})();function yp(e,t,r,n){for(let i=r;ie[i](...t)):e[i](...t);if(o instanceof Promise)return o.then(()=>yp(e,t,i+1,n))}catch(o){return Promise.reject(o)}}function O_(e,t,r){if(e.length>0)return yp(e,t,0,mp(r))}function x_(e,t,r){if(e.length>0){const n=mp(r);return Promise.all(e.map(i=>n.run(()=>i(...t))))}}function ha(e,t){for(const r of[...e])r(t)}var N_=class{_hooks;_before;_after;_deprecatedHooks;_deprecatedMessages;constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(e,t,r={}){if(!e||typeof t!="function")return()=>{};const n=e;let i;for(;this._deprecatedHooks[e];)i=this._deprecatedHooks[e],e=i.to;if(i&&!r.allowDeprecated){let o=i.message;o||(o=`${n} hook has been deprecated`+(i.to?`, please use ${i.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(o)||(console.warn(o),this._deprecatedMessages.add(o))}if(!t.name)try{Object.defineProperty(t,"name",{get:()=>"_"+e.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[e]=this._hooks[e]||[],this._hooks[e].push(t),()=>{t&&(this.removeHook(e,t),t=void 0)}}hookOnce(e,t){let r,n=(...i)=>(typeof r=="function"&&r(),r=void 0,n=void 0,t(...i));return r=this.hook(e,n),r}removeHook(e,t){const r=this._hooks[e];if(r){const n=r.indexOf(t);n!==-1&&r.splice(n,1),r.length===0&&(this._hooks[e]=void 0)}}deprecateHook(e,t){this._deprecatedHooks[e]=typeof t=="string"?{to:t}:t;const r=this._hooks[e]||[];this._hooks[e]=void 0;for(const n of r)this.hook(e,n)}deprecateHooks(e){for(const t in e)this.deprecateHook(t,e[t])}addHooks(e){const t=pu(e),r=Object.keys(t).map(n=>this.hook(n,t[n]));return()=>{for(const n of r)n();r.length=0}}removeHooks(e){const t=pu(e);for(const r in t)this.removeHook(r,t[r])}removeAllHooks(){this._hooks={}}callHook(e,...t){return this.callHookWith(O_,e,t)}callHookParallel(e,...t){return this.callHookWith(x_,e,t)}callHookWith(e,t,r){const n=this._before||this._after?{name:t,args:r,context:{}}:void 0;this._before&&ha(this._before,n);const i=e(this._hooks[t]?[...this._hooks[t]]:[],r,t);return i instanceof Promise?i.finally(()=>{this._after&&n&&ha(this._after,n)}):(this._after&&n&&ha(this._after,n),i)}beforeEach(e){return this._before=this._before||[],this._before.push(e),()=>{if(this._before!==void 0){const t=this._before.indexOf(e);t!==-1&&this._before.splice(t,1)}}}afterEach(e){return this._after=this._after||[],this._after.push(e),()=>{if(this._after!==void 0){const t=this._after.indexOf(e);t!==-1&&this._after.splice(t,1)}}}};function L_(){return new N_}const M_=["name","property","http-equiv"],D_=new Set(["viewport","description","keywords","robots"]);function bp(e){const t=e.split(":");return t.length?R_.has(t[1]):!1}function gu(e){const{props:t,tag:r}=e;if(I_.has(r))return r;if(r==="link"&&t.rel==="canonical")return"canonical";if(t.charset)return"charset";if(e.tag==="meta"){for(const n of M_)if(t[n]!==void 0){const i=t[n],o=i&&typeof i=="string"&&i.includes(":"),s=i&&D_.has(i),u=!(o||s)&&e.key?`:key:${e.key}`:"";return`${r}:${i}${u}`}}if(e.key)return`${r}:key:${e.key}`;if(t.id)return`${r}:id:${t.id}`;if(A_.has(r)){const n=e.textContent||e.innerHTML;if(n)return`${r}:content:${n}`}}function of(e){const t=e._h||e._d;if(t)return t;const r=e.textContent||e.innerHTML;return r||`${e.tag}:${Object.entries(e.props).map(([n,i])=>`${n}:${String(i)}`).join(",")}`}function ps(e,t,r){typeof e==="function"&&(!r||r!=="titleTemplate"&&!(r[0]==="o"&&r[1]==="n"))&&(e=e());const i=t?t(r,e):e;if(Array.isArray(i))return i.map(o=>ps(o,t));if(i?.constructor===Object){const o={};for(const s of Object.keys(i))o[s]=ps(i[s],t,s);return o}return i}function U_(e,t){const r=e==="style"?new Map:new Set;function n(i){if(i==null||i===void 0)return;const o=String(i).trim();if(o)if(e==="style"){const[s,...a]=o.split(":").map(u=>u?u.trim():"");s&&a.length&&r.set(s,a.join(":"))}else o.split(" ").filter(Boolean).forEach(s=>r.add(s))}return typeof t=="string"?e==="style"?t.split(";").forEach(n):n(t):Array.isArray(t)?t.forEach(i=>n(i)):t&&typeof t=="object"&&Object.entries(t).forEach(([i,o])=>{o&&o!=="false"&&(e==="style"?r.set(String(i).trim(),String(o)):n(i))}),r}function Ep(e,t){return e.props=e.props||{},t?e.tag==="templateParams"?(e.props=t,e):(Object.entries(t).forEach(([r,n])=>{if(n===null){e.props[r]=null;return}if(r==="class"||r==="style"){e.props[r]=U_(r,n);return}if(k_.has(r)){if(["textContent","innerHTML"].includes(r)&&typeof n=="object"){let a=t.type;if(t.type||(a="application/json"),!a?.endsWith("json")&&a!=="speculationrules")return;t.type=a,e.props.type=a,e[r]=JSON.stringify(n)}else e[r]=n;return}const i=String(n),o=r.startsWith("data-"),s=e.tag==="meta"&&r==="content";i==="true"||i===""?e.props[r]=o||s?i:!0:!n&&o&&i==="false"?e.props[r]="false":n!==void 0&&(e.props[r]=n)}),e):e}function F_(e,t){const r=typeof t=="object"&&typeof t!="function"?t:{[e==="script"||e==="noscript"||e==="style"?"innerHTML":"textContent"]:t},n=Ep({tag:e,props:{}},r);return n.key&&T_.has(n.tag)&&(n.props["data-hid"]=n._h=n.key),n.tag==="script"&&typeof n.innerHTML=="object"&&(n.innerHTML=JSON.stringify(n.innerHTML),n.props.type=n.props.type||"application/json"),Array.isArray(n.props.content)?n.props.content.map(i=>({...n,props:{...n.props,content:i}})):n}function H_(e,t){if(!e)return[];typeof e=="function"&&(e=e());const r=(i,o)=>{for(let s=0;s{if(o!==void 0)for(const s of Array.isArray(o)?o:[o])n.push(F_(i,s))}),n.flat()}const mu=(e,t)=>e._w===t._w?e._p-t._p:e._w-t._w,sf={base:-10,title:10},B_={critical:-8,high:-1,low:2},af={meta:{"content-security-policy":-30,charset:-20,viewport:-15},link:{preconnect:20,stylesheet:60,preload:70,modulepreload:70,prefetch:90,"dns-prefetch":90,prerender:90},script:{async:30,defer:80,sync:50},style:{imported:40,sync:60}},j_=/@import/,ai=e=>e===""||e===!0;function q_(e,t){if(typeof t.tagPriority=="number")return t.tagPriority;let r=100;const n=B_[t.tagPriority]||0,i=e.resolvedOptions.disableCapoSorting?{link:{},script:{},style:{}}:af;if(t.tag in sf)r=sf[t.tag];else if(t.tag==="meta"){const o=t.props["http-equiv"]==="content-security-policy"?"content-security-policy":t.props.charset?"charset":t.props.name==="viewport"?"viewport":null;o&&(r=af.meta[o])}else if(t.tag==="link"&&t.props.rel)r=i.link[t.props.rel];else if(t.tag==="script"){const o=String(t.props.type);ai(t.props.async)?r=i.script.async:t.props.src&&!ai(t.props.defer)&&!ai(t.props.async)&&o!=="module"&&!o.endsWith("json")||t.innerHTML&&!o.endsWith("json")?r=i.script.sync:(ai(t.props.defer)&&t.props.src&&!ai(t.props.async)||o==="module")&&(r=i.script.defer)}else t.tag==="style"&&(r=t.innerHTML&&j_.test(t.innerHTML)?i.style.imported:i.style.sync);return(r||100)+n}function uf(e,t){const r=typeof t=="function"?t(e):t,n=r.key||String(e.plugins.size+1);e.plugins.get(n)||(e.plugins.set(n,r),e.hooks.addHooks(r.hooks||{}))}function V_(e={}){const t=L_();t.addHooks(e.hooks||{});const r=!e.document,n=new Map,i=new Map,o=new Set,s={_entryCount:1,plugins:i,dirty:!1,resolvedOptions:e,hooks:t,ssr:r,entries:n,headEntries(){return[...n.values()]},use:a=>uf(s,a),push(a,u){const c={...u||{}};delete c.head;const l=c._index??s._entryCount++,f={_i:l,input:a,options:c},d={_poll(h=!1){s.dirty=!0,!h&&o.add(l),t.callHook("entries:updated",s)},dispose(){n.delete(l)&&s.invalidate()},patch(h){(!c.mode||c.mode==="server"&&r||c.mode==="client"&&!r)&&(f.input=h,n.set(l,f),d._poll())}};return d.patch(a),d},async resolveTags(){const a={tagMap:new Map,tags:[],entries:[...s.entries.values()]};for(await t.callHook("entries:resolve",a);o.size;){const d=o.values().next().value;o.delete(d);const h=n.get(d);if(h){const v={tags:H_(h.input,e.propResolvers||[]).map(p=>Object.assign(p,h.options)),entry:h};await t.callHook("entries:normalize",v),h._tags=v.tags.map((p,b)=>(p._w=q_(s,p),p._p=(h._i<<10)+b,p._d=gu(p),p))}}let u=!1;a.entries.flatMap(d=>(d._tags||[]).map(h=>({...h,props:{...h.props}}))).sort(mu).reduce((d,h)=>{const v=String(h._d||h._p);if(!d.has(v))return d.set(v,h);const p=d.get(v);if((h?.tagDuplicateStrategy||(P_.has(h.tag)?"merge":null)||(h.key&&h.key===p.key?"merge":null))==="merge"){const E={...p.props};Object.entries(h.props).forEach(([m,g])=>E[m]=m==="style"?new Map([...p.props.style||new Map,...g]):m==="class"?new Set([...p.props.class||new Set,...g]):g),d.set(v,{...h,props:E})}else h._p>>10===p._p>>10&&h.tag==="meta"&&bp(v)?(d.set(v,Object.assign([...Array.isArray(p)?p:[p],h],h)),u=!0):(h._w===p._w?h._p>p._p:h?._wuf(s,a)),s.hooks.callHook("init",s),e.init?.forEach(a=>a&&s.push(a)),s}const va="%separator";function $_(e,t,r=!1){let n;if(t==="s"||t==="pageTitle")n=e.pageTitle;else if(t.includes(".")){const i=t.indexOf(".");n=e[t.substring(0,i)]?.[t.substring(i+1)]}else n=e[t];if(n!==void 0)return r?(n||"").replace(/\\/g,"\\\\").replace(/{if(a===va||!o.includes(a))return a;const u=$_(t,a.slice(1),n);return u!==void 0?u:a}).trim(),s&&(e=e.split(va).map(a=>a.trim()).filter(a=>a!=="").join(r?` ${r} `:" ")),e}const lf=e=>e.includes(":key")?e:e.split(":").join(":key:"),W_={key:"aliasSorting",hooks:{"tags:resolve":e=>{let t=!1;for(const r of e.tags){const n=r.tagPriority;if(!n)continue;const i=String(n);if(i.startsWith("before:")){const o=lf(i.slice(7)),s=e.tagMap.get(o);s&&(typeof s.tagPriority=="number"&&(r.tagPriority=s.tagPriority),r._p=s._p-1,t=!0)}else if(i.startsWith("after:")){const o=lf(i.slice(6)),s=e.tagMap.get(o);s&&(typeof s.tagPriority=="number"&&(r.tagPriority=s.tagPriority),r._p=s._p+1,t=!0)}}t&&(e.tags=e.tags.sort(mu))}}},G_={key:"deprecations",hooks:{"entries:normalize":({tags:e})=>{for(const t of e)t.props.children&&(t.innerHTML=t.props.children,delete t.props.children),t.props.hid&&(t.key=t.props.hid,delete t.props.hid),t.props.vmid&&(t.key=t.props.vmid,delete t.props.vmid),t.props.body&&(t.tagPosition="bodyClose",delete t.props.body)}}};async function yu(e){if(typeof e==="function")return e;if(e instanceof Promise)return await e;if(Array.isArray(e))return await Promise.all(e.map(r=>yu(r)));if(e?.constructor===Object){const r={};for(const n of Object.keys(e))r[n]=await yu(e[n]);return r}return e}const K_={key:"promises",hooks:{"entries:resolve":async e=>{const t=[];for(const r in e.entries)e.entries[r]._promisesProcessed||t.push(yu(e.entries[r].input).then(n=>{e.entries[r].input=n,e.entries[r]._promisesProcessed=!0}));await Promise.all(t)}}},z_={meta:"content",link:"href",htmlAttrs:"lang"},X_=["innerHTML","textContent"],Y_=e=>({key:"template-params",hooks:{"entries:normalize":t=>{const r=t.tags.filter(n=>n.tag==="templateParams"&&n.mode==="server")?.[0]?.props||{};Object.keys(r).length&&(e._ssrPayload={templateParams:{...e._ssrPayload?.templateParams||{},...r}})},"tags:resolve":({tagMap:t,tags:r})=>{const n=t.get("templateParams")?.props||{},i=n.separator||"|";delete n.separator,n.pageTitle=yo(n.pageTitle||e._title||"",n,i);for(const o of r){if(o.processTemplateParams===!1)continue;const s=z_[o.tag];if(s&&typeof o.props[s]=="string")o.props[s]=yo(o.props[s],n,i);else if(o.processTemplateParams||o.tag==="titleTemplate"||o.tag==="title")for(const a of X_)typeof o[a]=="string"&&(o[a]=yo(o[a],n,i,o.tag==="script"&&o.props.type.endsWith("json")))}e._templateParams=n,e._separator=i},"tags:afterResolve":({tagMap:t})=>{const r=t.get("title");r?.textContent&&r.processTemplateParams!==!1&&(r.textContent=yo(r.textContent,e._templateParams,e._separator))}}}),J_=(e,t)=>Ve(t)?qy(t):t,kl="usehead";function Q_(e){return{install(r){r.config.globalProperties.$unhead=e,r.config.globalProperties.$head=e,r.provide(kl,e)}}.install}function Z_(){if(Os()){const e=_t(kl);if(!e)throw new Error("useHead() was called without provide context, ensure you call it through the setup() function.");return e}throw new Error("useHead() was called without provide context, ensure you call it through the setup() function.")}function eS(e,t={}){const r=t.head||Z_();return r.ssr?r.push(e||{},t):tS(r,e,t)}function tS(e,t,r={}){const n=Qt(!1);let i;return t0(()=>{const s=n.value?{}:ps(t,J_);i?i.patch(s):i=e.push(s,r)}),hn()&&(ei(()=>{i.dispose()}),gv(()=>{n.value=!0}),pv(()=>{n.value=!1})),i}function _p(e){const t=e||dp();return t?.ssrContext?.head||t?.runWithContext(()=>{if(Os())return _t(kl)})}function SN(e,t={}){const r=_p(t.nuxt);if(r)return eS(e,{head:r,...t})}const rS=(e,t)=>[],nS=e=>hp({},...rS().map(t=>t.data).reverse()),iS=nS;let Ho;function oS(){return Ho=$fetch(Il(`builds/meta/${ro().app.buildId}.json`),{responseType:"json"}),Ho.catch(e=>{console.error("[nuxt] Error fetching app manifest.",e)}),Ho}function Sp(){return Ho||oS()}function wp(e){const t=typeof e=="string"?e:e.path;try{return iS(t)}catch(r){return console.error("[nuxt] Error matching route rules.",r),{}}}async function cf(e,t={}){return null}async function sS(e){return null}let Gr=null;async function aS(){if(Gr)return Gr;const e=document.getElementById("__NUXT_DATA__");if(!e)return{};const t=await uS(e.textContent||""),r=e.dataset.src?await sS(e.dataset.src):void 0;return Gr={...t,...r,...window.__NUXT__},Gr.config?.public&&(Gr.config.public=Br(Gr.config.public)),Gr}async function uS(e){return await S_(e,He()._payloadRevivers)}function lS(e,t){He()._payloadRevivers[e]=t}const cS=[["NuxtError",e=>an(e)],["EmptyShallowRef",e=>br(e==="_"?void 0:e==="0n"?BigInt(0):ds(e))],["EmptyRef",e=>Qt(e==="_"?void 0:e==="0n"?BigInt(0):ds(e))],["ShallowRef",e=>br(e)],["ShallowReactive",e=>hr(e)],["Ref",e=>Qt(e)],["Reactive",e=>Br(e)]],fS=rr({name:"nuxt:revive-payload:client",order:-30,async setup(e){let t,r;for(const[n,i]of cS)lS(n,i);Object.assign(e.payload,([t,r]=Oi(()=>e.runWithContext(aS)),t=await t,r(),t)),window.__NUXT__=e.payload}});async function Pl(e,t={}){const r=t.document||e.resolvedOptions.document;if(!r||!e.dirty)return;const n={shouldRender:!0,tags:[]};if(await e.hooks.callHook("dom:beforeRender",n),!!n.shouldRender)return e._domUpdatePromise||(e._domUpdatePromise=new Promise(async i=>{const o=new Map,s=new Promise(h=>{e.resolveTags().then(v=>{h(v.map(p=>{const b=o.get(p._d)||0,E={tag:p,id:(b?`${p._d}:${b}`:p._d)||of(p),shouldRender:!0};return p._d&&bp(p._d)&&o.set(p._d,b+1),E}))})});let a=e._dom;if(!a){a={title:r.title,elMap:new Map().set("htmlAttrs",r.documentElement).set("bodyAttrs",r.body)};for(const h of["body","head"]){const v=r[h]?.children;for(const p of v){const b=p.tagName.toLowerCase();if(!nf.has(b))continue;const E=Ep({tag:b,props:{}},{innerHTML:p.innerHTML,...p.getAttributeNames().reduce((m,g)=>(m[g]=p.getAttribute(g),m),{})||{}});if(E.key=p.getAttribute("data-hid")||void 0,E._d=gu(E)||of(E),a.elMap.has(E._d)){let m=1,g=E._d;for(;a.elMap.has(g);)g=`${E._d}:${m++}`;a.elMap.set(g,p)}else a.elMap.set(E._d,p)}}}a.pendingSideEffects={...a.sideEffects},a.sideEffects={};function u(h,v,p){const b=`${h}:${v}`;a.sideEffects[b]=p,delete a.pendingSideEffects[b]}function c({id:h,$el:v,tag:p}){const b=p.tag.endsWith("Attrs");a.elMap.set(h,v),b||(p.textContent&&p.textContent!==v.textContent&&(v.textContent=p.textContent),p.innerHTML&&p.innerHTML!==v.innerHTML&&(v.innerHTML=p.innerHTML),u(h,"el",()=>{v?.remove(),a.elMap.delete(h)}));for(const E in p.props){if(!Object.prototype.hasOwnProperty.call(p.props,E))continue;const m=p.props[E];if(E.startsWith("on")&&typeof m=="function"){const y=v?.dataset;if(y&&y[`${E}fired`]){const _=E.slice(0,-5);m.call(v,new Event(_.substring(2)))}v.getAttribute(`data-${E}`)!==""&&((p.tag==="bodyAttrs"?r.defaultView:v).addEventListener(E.substring(2),m.bind(v)),v.setAttribute(`data-${E}`,""));continue}const g=`attr:${E}`;if(E==="class"){if(!m)continue;for(const y of m)b&&u(h,`${g}:${y}`,()=>v.classList.remove(y)),!v.classList.contains(y)&&v.classList.add(y)}else if(E==="style"){if(!m)continue;for(const[y,_]of m)u(h,`${g}:${y}`,()=>{v.style.removeProperty(y)}),v.style.setProperty(y,_)}else m!==!1&&m!==null&&(v.getAttribute(E)!==m&&v.setAttribute(E,m===!0?"":String(m)),b&&u(h,g,()=>v.removeAttribute(E)))}}const l=[],f={bodyClose:void 0,bodyOpen:void 0,head:void 0},d=await s;for(const h of d){const{tag:v,shouldRender:p,id:b}=h;if(p){if(v.tag==="title"){r.title=v.textContent,u("title","",()=>r.title=a.title);continue}h.$el=h.$el||a.elMap.get(b),h.$el?c(h):nf.has(v.tag)&&l.push(h)}}for(const h of l){const v=h.tag.tagPosition||"head";h.$el=r.createElement(h.tag.tag),c(h),f[v]=f[v]||r.createDocumentFragment(),f[v].appendChild(h.$el)}for(const h of d)await e.hooks.callHook("dom:renderTag",h,r,u);f.head&&r.head.appendChild(f.head),f.bodyOpen&&r.body.insertBefore(f.bodyOpen,r.body.firstChild),f.bodyClose&&r.body.appendChild(f.bodyClose);for(const h in a.pendingSideEffects)a.pendingSideEffects[h]();e._dom=a,await e.hooks.callHook("dom:rendered",{renders:d}),i()}).finally(()=>{e._domUpdatePromise=void 0,e.dirty=!1})),e._domUpdatePromise}function dS(e={}){const t=e.domOptions?.render||Pl;e.document=e.document||(typeof window<"u"?document:void 0);const r=e.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML||!1;return V_({...e,plugins:[...e.plugins||[],{key:"client",hooks:{"entries:updated":t}}],init:[r?JSON.parse(r):!1,...e.init||[]]})}function hS(e,t){let r=0;return()=>{const n=++r;t(()=>{r===n&&e()})}}function vS(e={}){const t=dS({domOptions:{render:hS(()=>Pl(t),r=>setTimeout(r,0))},...e});return t.install=Q_(t),t}const pS={disableDefaults:!0,disableCapoSorting:!1,plugins:[G_,K_,Y_,W_]},gS=rr({name:"nuxt:head",enforce:"pre",setup(e){const t=vS(pS);e.vueApp.use(t);{let r=!0;const n=async()=>{r=!1,await Pl(t)};t.hooks.hook("dom:beforeRender",i=>{i.shouldRender=!r}),e.hooks.hook("page:start",()=>{r=!0}),e.hooks.hook("page:finish",()=>{e.isHydrating||n()}),e.hooks.hook("app:error",n),e.hooks.hook("app:suspense:resolve",n)}}});const wn=typeof document<"u";function Tp(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function mS(e){return e.__esModule||e[Symbol.toStringTag]==="Module"||e.default&&Tp(e.default)}const Ce=Object.assign;function pa(e,t){const r={};for(const n in t){const i=t[n];r[n]=Vt(i)?i.map(e):e(i)}return r}const xi=()=>{},Vt=Array.isArray;function ff(e,t){const r={};for(const n in e)r[n]=n in t?t[n]:e[n];return r}const Ap=/#/g,yS=/&/g,bS=/\//g,ES=/=/g,_S=/\?/g,Cp=/\+/g,SS=/%5B/g,wS=/%5D/g,Ip=/%5E/g,TS=/%60/g,kp=/%7B/g,AS=/%7C/g,Pp=/%7D/g,CS=/%20/g;function Rl(e){return e==null?"":encodeURI(""+e).replace(AS,"|").replace(SS,"[").replace(wS,"]")}function IS(e){return Rl(e).replace(kp,"{").replace(Pp,"}").replace(Ip,"^")}function bu(e){return Rl(e).replace(Cp,"%2B").replace(CS,"+").replace(Ap,"%23").replace(yS,"%26").replace(TS,"`").replace(kp,"{").replace(Pp,"}").replace(Ip,"^")}function kS(e){return bu(e).replace(ES,"%3D")}function PS(e){return Rl(e).replace(Ap,"%23").replace(_S,"%3F")}function RS(e){return PS(e).replace(bS,"%2F")}function Xi(e){if(e==null)return null;try{return decodeURIComponent(""+e)}catch{}return""+e}const OS=/\/$/,xS=e=>e.replace(OS,"");function ga(e,t,r="/"){let n,i={},o="",s="";const a=t.indexOf("#");let u=t.indexOf("?");return u=a>=0&&u>a?-1:u,u>=0&&(n=t.slice(0,u),o=t.slice(u,a>0?a:t.length),i=e(o.slice(1))),a>=0&&(n=n||t.slice(0,a),s=t.slice(a,t.length)),n=DS(n??t,r),{fullPath:n+o+s,path:n,query:i,hash:Xi(s)}}function NS(e,t){const r=t.query?e(t.query):"";return t.path+(r&&"?")+r+(t.hash||"")}function df(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function LS(e,t,r){const n=t.matched.length-1,i=r.matched.length-1;return n>-1&&n===i&&Kn(t.matched[n],r.matched[i])&&Rp(t.params,r.params)&&e(t.query)===e(r.query)&&t.hash===r.hash}function Kn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Rp(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var r in e)if(!MS(e[r],t[r]))return!1;return!0}function MS(e,t){return Vt(e)?hf(e,t):Vt(t)?hf(t,e):e?.valueOf()===t?.valueOf()}function hf(e,t){return Vt(t)?e.length===t.length&&e.every((r,n)=>r===t[n]):e.length===1&&e[0]===t}function DS(e,t){if(e.startsWith("/"))return e;if(!e)return t;const r=t.split("/"),n=e.split("/"),i=n[n.length-1];(i===".."||i===".")&&n.push("");let o=r.length-1,s,a;for(s=0;s1&&o--;else break;return r.slice(0,o).join("/")+"/"+n.slice(s).join("/")}const Pt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};let Eu=(function(e){return e.pop="pop",e.push="push",e})({}),ma=(function(e){return e.back="back",e.forward="forward",e.unknown="",e})({});function US(e){if(!e)if(wn){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),xS(e)}const FS=/^[^#]+#/;function HS(e,t){return e.replace(FS,"#")+t}function BS(e,t){const r=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-r.left-(t.left||0),top:n.top-r.top-(t.top||0)}}const Fs=()=>({left:window.scrollX,top:window.scrollY});function jS(e){let t;if("el"in e){const r=e.el,n=typeof r=="string"&&r.startsWith("#"),i=typeof r=="string"?n?document.getElementById(r.slice(1)):document.querySelector(r):r;if(!i)return;t=BS(i,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function vf(e,t){return(history.state?history.state.position-t:-1)+e}const _u=new Map;function qS(e,t){_u.set(e,t)}function VS(e){const t=_u.get(e);return _u.delete(e),t}function $S(e){return typeof e=="string"||e&&typeof e=="object"}function Op(e){return typeof e=="string"||typeof e=="symbol"}let je=(function(e){return e[e.MATCHER_NOT_FOUND=1]="MATCHER_NOT_FOUND",e[e.NAVIGATION_GUARD_REDIRECT=2]="NAVIGATION_GUARD_REDIRECT",e[e.NAVIGATION_ABORTED=4]="NAVIGATION_ABORTED",e[e.NAVIGATION_CANCELLED=8]="NAVIGATION_CANCELLED",e[e.NAVIGATION_DUPLICATED=16]="NAVIGATION_DUPLICATED",e})({});const xp=Symbol("");je.MATCHER_NOT_FOUND+"",je.NAVIGATION_GUARD_REDIRECT+"",je.NAVIGATION_ABORTED+"",je.NAVIGATION_CANCELLED+"",je.NAVIGATION_DUPLICATED+"";function zn(e,t){return Ce(new Error,{type:e,[xp]:!0},t)}function or(e,t){return e instanceof Error&&xp in e&&(t==null||!!(e.type&t))}const WS=["params","query","hash"];function GS(e){if(typeof e=="string")return e;if(e.path!=null)return e.path;const t={};for(const r of WS)r in e&&(t[r]=e[r]);return JSON.stringify(t,null,2)}function KS(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let n=0;ni&&bu(i)):[n&&bu(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+r,i!=null&&(t+="="+i))})}return t}function zS(e){const t={};for(const r in e){const n=e[r];n!==void 0&&(t[r]=Vt(n)?n.map(i=>i==null?null:""+i):n==null?n:""+n)}return t}const XS=Symbol(""),gf=Symbol(""),Ol=Symbol(""),Np=Symbol(""),Su=Symbol("");function ui(){let e=[];function t(n){return e.push(n),()=>{const i=e.indexOf(n);i>-1&&e.splice(i,1)}}function r(){e=[]}return{add:t,list:()=>e.slice(),reset:r}}function Ir(e,t,r,n,i,o=s=>s()){const s=n&&(n.enterCallbacks[i]=n.enterCallbacks[i]||[]);return()=>new Promise((a,u)=>{const c=d=>{d===!1?u(zn(je.NAVIGATION_ABORTED,{from:r,to:t})):d instanceof Error?u(d):$S(d)?u(zn(je.NAVIGATION_GUARD_REDIRECT,{from:t,to:d})):(s&&n.enterCallbacks[i]===s&&typeof d=="function"&&s.push(d),a())},l=o(()=>e.call(n&&n.instances[i],t,r,c));let f=Promise.resolve(l);e.length<3&&(f=f.then(c)),f.catch(d=>u(d))})}function ya(e,t,r,n,i=o=>o()){const o=[];for(const s of e)for(const a in s.components){let u=s.components[a];if(!(t!=="beforeRouteEnter"&&!s.instances[a]))if(Tp(u)){const c=(u.__vccOpts||u)[t];c&&o.push(Ir(c,r,n,s,a,i))}else{let c=u();o.push(()=>c.then(l=>{if(!l)throw new Error(`Couldn't resolve component "${a}" at "${s.path}"`);const f=mS(l)?l.default:l;s.mods[a]=l,s.components[a]=f;const d=(f.__vccOpts||f)[t];return d&&Ir(d,r,n,s,a,i)()}))}}return o}function YS(e,t){const r=[],n=[],i=[],o=Math.max(t.matched.length,e.matched.length);for(let s=0;sKn(c,a))?n.push(a):r.push(a));const u=e.matched[s];u&&(t.matched.find(c=>Kn(c,u))||i.push(u))}return[r,n,i]}let JS=()=>location.protocol+"//"+location.host;function Lp(e,t){const{pathname:r,search:n,hash:i}=t,o=e.indexOf("#");if(o>-1){let s=i.includes(e.slice(o))?e.slice(o).length:1,a=i.slice(s);return a[0]!=="/"&&(a="/"+a),df(a,"")}return df(r,e)+n+i}function QS(e,t,r,n){let i=[],o=[],s=null;const a=({state:d})=>{const h=Lp(e,location),v=r.value,p=t.value;let b=0;if(d){if(r.value=h,t.value=d,s&&s===v){s=null;return}b=p?d.position-p.position:0}else n(h);i.forEach(E=>{E(r.value,v,{delta:b,type:Eu.pop,direction:b?b>0?ma.forward:ma.back:ma.unknown})})};function u(){s=r.value}function c(d){i.push(d);const h=()=>{const v=i.indexOf(d);v>-1&&i.splice(v,1)};return o.push(h),h}function l(){if(document.visibilityState==="hidden"){const{history:d}=window;if(!d.state)return;d.replaceState(Ce({},d.state,{scroll:Fs()}),"")}}function f(){for(const d of o)d();o=[],window.removeEventListener("popstate",a),window.removeEventListener("pagehide",l),document.removeEventListener("visibilitychange",l)}return window.addEventListener("popstate",a),window.addEventListener("pagehide",l),document.addEventListener("visibilitychange",l),{pauseListeners:u,listen:c,destroy:f}}function mf(e,t,r,n=!1,i=!1){return{back:e,current:t,forward:r,replaced:n,position:window.history.length,scroll:i?Fs():null}}function ZS(e){const{history:t,location:r}=window,n={value:Lp(e,r)},i={value:t.state};i.value||o(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(u,c,l){const f=e.indexOf("#"),d=f>-1?(r.host&&document.querySelector("base")?e:e.slice(f))+u:JS()+e+u;try{t[l?"replaceState":"pushState"](c,"",d),i.value=c}catch(h){console.error(h),r[l?"replace":"assign"](d)}}function s(u,c){o(u,Ce({},t.state,mf(i.value.back,u,i.value.forward,!0),c,{position:i.value.position}),!0),n.value=u}function a(u,c){const l=Ce({},i.value,t.state,{forward:u,scroll:Fs()});o(l.current,l,!0),o(u,Ce({},mf(n.value,u,null),{position:l.position+1},c),!1),n.value=u}return{location:n,state:i,push:a,replace:s}}function ew(e){e=US(e);const t=ZS(e),r=QS(e,t.state,t.location,t.replace);function n(o,s=!0){s||r.pauseListeners(),history.go(o)}const i=Ce({location:"",base:e,go:n,createHref:HS.bind(null,e)},t,r);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}let Zr=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.Group=2]="Group",e})({});var ze=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.ParamRegExp=2]="ParamRegExp",e[e.ParamRegExpEnd=3]="ParamRegExpEnd",e[e.EscapeNext=4]="EscapeNext",e})(ze||{});const tw={type:Zr.Static,value:""},rw=/[a-zA-Z0-9_]/;function nw(e){if(!e)return[[]];if(e==="/")return[[tw]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${r})/"${c}": ${h}`)}let r=ze.Static,n=r;const i=[];let o;function s(){o&&i.push(o),o=[]}let a=0,u,c="",l="";function f(){c&&(r===ze.Static?o.push({type:Zr.Static,value:c}):r===ze.Param||r===ze.ParamRegExp||r===ze.ParamRegExpEnd?(o.length>1&&(u==="*"||u==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),o.push({type:Zr.Param,value:c,regexp:l,repeatable:u==="*"||u==="+",optional:u==="*"||u==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=u}for(;at.length?t.length===1&&t[0]===ht.Static+ht.Segment?1:-1:0}function Mp(e,t){let r=0;const n=e.score,i=t.score;for(;r0&&t[t.length-1]<0}const uw={strict:!1,end:!0,sensitive:!1};function lw(e,t,r){const n=sw(nw(e.path),r),i=Ce(n,{record:e,parent:t,children:[],alias:[]});return t&&!i.record.aliasOf==!t.record.aliasOf&&t.children.push(i),i}function cw(e,t){const r=[],n=new Map;t=ff(uw,t);function i(f){return n.get(f)}function o(f,d,h){const v=!h,p=_f(f);p.aliasOf=h&&h.record;const b=ff(t,f),E=[p];if("alias"in f){const y=typeof f.alias=="string"?[f.alias]:f.alias;for(const _ of y)E.push(_f(Ce({},p,{components:h?h.record.components:p.components,path:_,aliasOf:h?h.record:p})))}let m,g;for(const y of E){const{path:_}=y;if(d&&_[0]!=="/"){const S=d.record.path,A=S[S.length-1]==="/"?"":"/";y.path=d.record.path+(_&&A+_)}if(m=lw(y,d,b),h?h.alias.push(m):(g=g||m,g!==m&&g.alias.push(m),v&&f.name&&!Sf(m)&&s(f.name)),Dp(m)&&u(m),p.children){const S=p.children;for(let A=0;A{s(g)}:xi}function s(f){if(Op(f)){const d=n.get(f);d&&(n.delete(f),r.splice(r.indexOf(d),1),d.children.forEach(s),d.alias.forEach(s))}else{const d=r.indexOf(f);d>-1&&(r.splice(d,1),f.record.name&&n.delete(f.record.name),f.children.forEach(s),f.alias.forEach(s))}}function a(){return r}function u(f){const d=hw(f,r);r.splice(d,0,f),f.record.name&&!Sf(f)&&n.set(f.record.name,f)}function c(f,d){let h,v={},p,b;if("name"in f&&f.name){if(h=n.get(f.name),!h)throw zn(je.MATCHER_NOT_FOUND,{location:f});b=h.record.name,v=Ce(Ef(d.params,h.keys.filter(g=>!g.optional).concat(h.parent?h.parent.keys.filter(g=>g.optional):[]).map(g=>g.name)),f.params&&Ef(f.params,h.keys.map(g=>g.name))),p=h.stringify(v)}else if(f.path!=null)p=f.path,h=r.find(g=>g.re.test(p)),h&&(v=h.parse(p),b=h.record.name);else{if(h=d.name?n.get(d.name):r.find(g=>g.re.test(d.path)),!h)throw zn(je.MATCHER_NOT_FOUND,{location:f,currentLocation:d});b=h.record.name,v=Ce({},d.params,f.params),p=h.stringify(v)}const E=[];let m=h;for(;m;)E.unshift(m.record),m=m.parent;return{name:b,path:p,params:v,matched:E,meta:dw(E)}}e.forEach(f=>o(f));function l(){r.length=0,n.clear()}return{addRoute:o,resolve:c,removeRoute:s,clearRoutes:l,getRoutes:a,getRecordMatcher:i}}function Ef(e,t){const r={};for(const n of t)n in e&&(r[n]=e[n]);return r}function _f(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:fw(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function fw(e){const t={},r=e.props||!1;if("component"in e)t.default=r;else for(const n in e.components)t[n]=typeof r=="object"?r[n]:r;return t}function Sf(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function dw(e){return e.reduce((t,r)=>Ce(t,r.meta),{})}function hw(e,t){let r=0,n=t.length;for(;r!==n;){const o=r+n>>1;Mp(e,t[o])<0?n=o:r=o+1}const i=vw(e);return i&&(n=t.lastIndexOf(i,n-1)),n}function vw(e){let t=e;for(;t=t.parent;)if(Dp(t)&&Mp(e,t)===0)return t}function Dp({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function wf(e){const t=_t(Ol),r=_t(Np),n=We(()=>{const u=ke(e.to);return t.resolve(u)}),i=We(()=>{const{matched:u}=n.value,{length:c}=u,l=u[c-1],f=r.matched;if(!l||!f.length)return-1;const d=f.findIndex(Kn.bind(null,l));if(d>-1)return d;const h=Tf(u[c-2]);return c>1&&Tf(l)===h&&f[f.length-1].path!==h?f.findIndex(Kn.bind(null,u[c-2])):d}),o=We(()=>i.value>-1&&bw(r.params,n.value.params)),s=We(()=>i.value>-1&&i.value===r.matched.length-1&&Rp(r.params,n.value.params));function a(u={}){if(yw(u)){const c=t[ke(e.replace)?"replace":"push"](ke(e.to)).catch(xi);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:n,href:We(()=>n.value.href),isActive:o,isExactActive:s,navigate:a}}function pw(e){return e.length===1?e[0]:e}const gw=jr({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:wf,setup(e,{slots:t}){const r=Br(wf(e)),{options:n}=_t(Ol),i=We(()=>({[Af(e.activeClass,n.linkActiveClass,"router-link-active")]:r.isActive,[Af(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:r.isExactActive}));return()=>{const o=t.default&&pw(t.default(r));return e.custom?o:Ke("a",{"aria-current":r.isExactActive?e.ariaCurrentValue:null,href:r.href,onClick:r.navigate,class:i.value},o)}}}),mw=gw;function yw(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function bw(e,t){for(const r in t){const n=t[r],i=e[r];if(typeof n=="string"){if(n!==i)return!1}else if(!Vt(i)||i.length!==n.length||n.some((o,s)=>o.valueOf()!==i[s].valueOf()))return!1}return!0}function Tf(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Af=(e,t,r)=>e??t??r,Ew=jr({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:r}){const n=_t(Su),i=We(()=>e.route||n.value),o=_t(gf,0),s=We(()=>{let c=ke(o);const{matched:l}=i.value;let f;for(;(f=l[c])&&!f.components;)c++;return c}),a=We(()=>i.value.matched[s.value]);Ln(gf,We(()=>s.value+1)),Ln(XS,a),Ln(Su,i);const u=Qt();return Mn(()=>[u.value,a.value,e.name],([c,l,f],[d,h,v])=>{l&&(l.instances[f]=c,h&&h!==l&&c&&c===d&&(l.leaveGuards.size||(l.leaveGuards=h.leaveGuards),l.updateGuards.size||(l.updateGuards=h.updateGuards))),c&&l&&(!h||!Kn(l,h)||!d)&&(l.enterCallbacks[f]||[]).forEach(p=>p(c))},{flush:"post"}),()=>{const c=i.value,l=e.name,f=a.value,d=f&&f.components[l];if(!d)return Cf(r.default,{Component:d,route:c});const h=f.props[l],v=h?h===!0?c.params:typeof h=="function"?h(c):h:null,b=Ke(d,Ce({},v,t,{onVnodeUnmounted:E=>{E.component.isUnmounted&&(f.instances[l]=null)},ref:u}));return Cf(r.default,{Component:b,route:c})||b}}});function Cf(e,t){if(!e)return null;const r=e(t);return r.length===1?r[0]:r}const Up=Ew;function _w(e){const t=cw(e.routes,e),r=e.parseQuery||KS,n=e.stringifyQuery||pf,i=e.history,o=ui(),s=ui(),a=ui(),u=br(Pt);let c=Pt;wn&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const l=pa.bind(null,M=>""+M),f=pa.bind(null,RS),d=pa.bind(null,Xi);function h(M,Z){let X,re;return Op(M)?(X=t.getRecordMatcher(M),re=Z):re=M,t.addRoute(re,X)}function v(M){const Z=t.getRecordMatcher(M);Z&&t.removeRoute(Z)}function p(){return t.getRoutes().map(M=>M.record)}function b(M){return!!t.getRecordMatcher(M)}function E(M,Z){if(Z=Ce({},Z||u.value),typeof M=="string"){const T=ga(r,M,Z.path),L=t.resolve({path:T.path},Z),H=i.createHref(T.fullPath);return Ce(T,L,{params:d(L.params),hash:Xi(T.hash),redirectedFrom:void 0,href:H})}let X;if(M.path!=null)X=Ce({},M,{path:ga(r,M.path,Z.path).path});else{const T=Ce({},M.params);for(const L in T)T[L]==null&&delete T[L];X=Ce({},M,{params:f(T)}),Z.params=f(Z.params)}const re=t.resolve(X,Z),F=M.hash||"";re.params=l(d(re.params));const ge=NS(n,Ce({},M,{hash:IS(F),path:re.path})),w=i.createHref(ge);return Ce({fullPath:ge,hash:F,query:n===pf?zS(M.query):M.query||{}},re,{redirectedFrom:void 0,href:w})}function m(M){return typeof M=="string"?ga(r,M,u.value.path):Ce({},M)}function g(M,Z){if(c!==M)return zn(je.NAVIGATION_CANCELLED,{from:Z,to:M})}function y(M){return A(M)}function _(M){return y(Ce(m(M),{replace:!0}))}function S(M,Z){const X=M.matched[M.matched.length-1];if(X&&X.redirect){const{redirect:re}=X;let F=typeof re=="function"?re(M,Z):re;return typeof F=="string"&&(F=F.includes("?")||F.includes("#")?F=m(F):{path:F},F.params={}),Ce({query:M.query,hash:M.hash,params:F.path!=null?{}:M.params},F)}}function A(M,Z){const X=c=E(M),re=u.value,F=M.state,ge=M.force,w=M.replace===!0,T=S(X,re);if(T)return A(Ce(m(T),{state:typeof T=="object"?Ce({},F,T.state):F,force:ge,replace:w}),Z||X);const L=X;L.redirectedFrom=Z;let H;return!ge&&LS(n,re,X)&&(H=zn(je.NAVIGATION_DUPLICATED,{to:L,from:re}),fe(re,re,!0,!1)),(H?Promise.resolve(H):P(L,re)).catch(U=>or(U)?or(U,je.NAVIGATION_GUARD_REDIRECT)?U:ae(U):K(U,L,re)).then(U=>{if(U){if(or(U,je.NAVIGATION_GUARD_REDIRECT))return A(Ce({replace:w},m(U.to),{state:typeof U.to=="object"?Ce({},F,U.to.state):F,force:ge}),Z||L)}else U=k(L,re,!0,w,F);return R(L,re,U),U})}function C(M,Z){const X=g(M,Z);return X?Promise.reject(X):Promise.resolve()}function I(M){const Z=be.values().next().value;return Z&&typeof Z.runWithContext=="function"?Z.runWithContext(M):M()}function P(M,Z){let X;const[re,F,ge]=YS(M,Z);X=ya(re.reverse(),"beforeRouteLeave",M,Z);for(const T of re)T.leaveGuards.forEach(L=>{X.push(Ir(L,M,Z))});const w=C.bind(null,M,Z);return X.push(w),me(X).then(()=>{X=[];for(const T of o.list())X.push(Ir(T,M,Z));return X.push(w),me(X)}).then(()=>{X=ya(F,"beforeRouteUpdate",M,Z);for(const T of F)T.updateGuards.forEach(L=>{X.push(Ir(L,M,Z))});return X.push(w),me(X)}).then(()=>{X=[];for(const T of ge)if(T.beforeEnter)if(Vt(T.beforeEnter))for(const L of T.beforeEnter)X.push(Ir(L,M,Z));else X.push(Ir(T.beforeEnter,M,Z));return X.push(w),me(X)}).then(()=>(M.matched.forEach(T=>T.enterCallbacks={}),X=ya(ge,"beforeRouteEnter",M,Z,I),X.push(w),me(X))).then(()=>{X=[];for(const T of s.list())X.push(Ir(T,M,Z));return X.push(w),me(X)}).catch(T=>or(T,je.NAVIGATION_CANCELLED)?T:Promise.reject(T))}function R(M,Z,X){a.list().forEach(re=>I(()=>re(M,Z,X)))}function k(M,Z,X,re,F){const ge=g(M,Z);if(ge)return ge;const w=Z===Pt,T=wn?history.state:{};X&&(re||w?i.replace(M.fullPath,Ce({scroll:w&&T&&T.scroll},F)):i.push(M.fullPath,F)),u.value=M,fe(M,Z,X,w),ae()}let N;function $(){N||(N=i.listen((M,Z,X)=>{if(!de.listening)return;const re=E(M),F=S(re,de.currentRoute.value);if(F){A(Ce(F,{replace:!0,force:!0}),re).catch(xi);return}c=re;const ge=u.value;wn&&qS(vf(ge.fullPath,X.delta),Fs()),P(re,ge).catch(w=>or(w,je.NAVIGATION_ABORTED|je.NAVIGATION_CANCELLED)?w:or(w,je.NAVIGATION_GUARD_REDIRECT)?(A(Ce(m(w.to),{force:!0}),re).then(T=>{or(T,je.NAVIGATION_ABORTED|je.NAVIGATION_DUPLICATED)&&!X.delta&&X.type===Eu.pop&&i.go(-1,!1)}).catch(xi),Promise.reject()):(X.delta&&i.go(-X.delta,!1),K(w,re,ge))).then(w=>{w=w||k(re,ge,!1),w&&(X.delta&&!or(w,je.NAVIGATION_CANCELLED)?i.go(-X.delta,!1):X.type===Eu.pop&&or(w,je.NAVIGATION_ABORTED|je.NAVIGATION_DUPLICATED)&&i.go(-1,!1)),R(re,ge,w)}).catch(xi)}))}let ee=ui(),B=ui(),z;function K(M,Z,X){ae(M);const re=B.list();return re.length?re.forEach(F=>F(M,Z,X)):console.error(M),Promise.reject(M)}function Q(){return z&&u.value!==Pt?Promise.resolve():new Promise((M,Z)=>{ee.add([M,Z])})}function ae(M){return z||(z=!M,$(),ee.list().forEach(([Z,X])=>M?X(M):Z()),ee.reset()),M}function fe(M,Z,X,re){const{scrollBehavior:F}=e;if(!wn||!F)return Promise.resolve();const ge=!X&&VS(vf(M.fullPath,0))||(re||!X)&&history.state&&history.state.scroll||null;return Vi().then(()=>F(M,Z,ge)).then(w=>w&&jS(w)).catch(w=>K(w,M,Z))}const pe=M=>i.go(M);let ye;const be=new Set,de={currentRoute:u,listening:!0,addRoute:h,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:b,getRoutes:p,resolve:E,options:e,push:y,replace:_,go:pe,back:()=>pe(-1),forward:()=>pe(1),beforeEach:o.add,beforeResolve:s.add,afterEach:a.add,onError:B.add,isReady:Q,install(M){M.component("RouterLink",mw),M.component("RouterView",Up),M.config.globalProperties.$router=de,Object.defineProperty(M.config.globalProperties,"$route",{enumerable:!0,get:()=>ke(u)}),wn&&!ye&&u.value===Pt&&(ye=!0,y(i.location).catch(re=>{}));const Z={};for(const re in Pt)Object.defineProperty(Z,re,{get:()=>u.value[re],enumerable:!0});M.provide(Ol,de),M.provide(Np,hr(Z)),M.provide(Su,u);const X=M.unmount;be.add(M),M.unmount=function(){be.delete(M),be.size<1&&(c=Pt,N&&N(),N=null,u.value=Pt,ye=!1,z=!1),X()}}};function me(M){return M.reduce((Z,X)=>Z.then(()=>I(X)),Promise.resolve())}return de}const Sw=/(:\w+)\([^)]+\)/g,ww=/(:\w+)[?+*]/g,Tw=/:\w+/g,Aw=(e,t)=>t.path.replace(Sw,"$1").replace(ww,"$1").replace(Tw,r=>e.params[r.slice(1)]?.toString()||""),wu=(e,t)=>{const r=e.route.matched.find(i=>i.components?.default===e.Component.type),n=t??r?.meta.key??(r&&Aw(e.route,r));return typeof n=="function"?n(e.route):n},Cw=(e,t)=>({default:()=>e?Ke(h0,e===!0?{}:e,t):t});function xl(e){return Array.isArray(e)?e:[e]}const Iw="modulepreload",kw=function(e,t){return new URL(e,t).href},If={},Yt=function(t,r,n){let i=Promise.resolve();if(r&&r.length>0){let c=function(l){return Promise.all(l.map(f=>Promise.resolve(f).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const s=document.getElementsByTagName("link"),a=document.querySelector("meta[property=csp-nonce]"),u=a?.nonce||a?.getAttribute("nonce");i=c(r.map(l=>{if(l=kw(l,n),l in If)return;If[l]=!0;const f=l.endsWith(".css"),d=f?'[rel="stylesheet"]':"";if(n)for(let v=s.length-1;v>=0;v--){const p=s[v];if(p.href===l&&(!f||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${l}"]${d}`))return;const h=document.createElement("link");if(h.rel=f?"stylesheet":Iw,f||(h.as="script"),h.crossOrigin="",h.href=l,u&&h.setAttribute("nonce",u),document.head.appendChild(h),f)return new Promise((v,p)=>{h.addEventListener("load",v),h.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${l}`)))})}))}function o(s){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=s,window.dispatchEvent(a),!a.defaultPrevented)throw s}return i.then(s=>{for(const a of s||[])a.status==="rejected"&&o(a.reason);return t().catch(o)})},ba=[{name:"index",path:"/",component:()=>Yt(()=>import("./B77IIWzB.js"),[],import.meta.url)},{name:"burrito",path:"/burrito",component:()=>Yt(()=>import("./MmvQX_eT.js"),[],import.meta.url)},{name:"profile",path:"/profile",component:()=>Yt(()=>import("./C_TnQn_7.js"),[],import.meta.url)}],Pw=(e,t)=>({default:()=>e?Ke(pb,e===!0?{}:e,t):t.default?.()}),Rw=/(:\w+)\([^)]+\)/g,Ow=/(:\w+)[?+*]/g,xw=/:\w+/g;function kf(e){const t=e?.meta.key??e.path.replace(Rw,"$1").replace(Ow,"$1").replace(xw,r=>e.params[r.slice(1)]?.toString()||"");return typeof t=="function"?t(e):t}function Nw(e,t){return e===t||t===Pt?!1:kf(e)!==kf(t)?!0:!e.matched.every((n,i)=>n.components&&n.components.default===t.matched[i]?.components?.default)}function Lw(e,t=!1){if(e){if(e.nodeName==="#comment"&&e.nodeValue==="[")return Fp(e,[],t);if(t){const r=e.cloneNode(!0);return r.querySelectorAll("[data-island-slot]").forEach(n=>{n.innerHTML=""}),[r.outerHTML]}return[e.outerHTML]}}function Fp(e,t=[],r=!1){if(e&&e.nodeName){if(Dw(e))return t;if(!Mw(e)){const n=e.cloneNode(!0);r&&n.querySelectorAll?.("[data-island-slot]").forEach(i=>{i.innerHTML=""}),t.push(n.outerHTML)}Fp(e.nextSibling,t,r)}return t}function Ea(e,t){const r=e?Lw(e):[t];return r?Q0(r.join(""),r.length):Ke("div")}function Mw(e){return e.nodeName==="#comment"&&e.nodeValue==="["}function Dw(e){return e.nodeName==="#comment"&&e.nodeValue==="]"}const Uw={scrollBehavior(e,t,r){const n=He(),i=wt().options?.scrollBehaviorType??"auto";if(e.path.replace(/\/$/,"")===t.path.replace(/\/$/,""))return t.hash&&!e.hash?{left:0,top:0}:e.hash?{el:e.hash,top:Hp(e.hash),behavior:i}:!1;if((typeof e.meta.scrollToTop=="function"?e.meta.scrollToTop(e,t):e.meta.scrollToTop)===!1)return!1;const s=n._runningTransition?"page:transition:finish":"page:loading:end";return new Promise(a=>{if(t===Pt){a(Pf(e,t,r,i));return}n.hooks.hookOnce(s,()=>{requestAnimationFrame(()=>a(Pf(e,t,r,i)))})})}};function Hp(e){try{const t=document.querySelector(e);if(t)return(Number.parseFloat(getComputedStyle(t).scrollMarginTop)||0)+(Number.parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop)||0)}catch{}return 0}function Pf(e,t,r,n){if(r)return r;const i=Nw(e,t);return e.hash?{el:e.hash,top:Hp(e.hash),behavior:i?n:"instant"}:{left:0,top:0}}const Fw={hashMode:!1,scrollBehaviorType:"auto"},Sr={...Fw,...Uw},Hw=async(e,t)=>{let r,n;if(!e.meta?.validate)return;const i=([r,n]=Oi(()=>Promise.resolve(e.meta.validate(e))),r=await r,n(),r);if(i===!0)return;const o=an({fatal:!0,status:i&&(i.status||i.statusCode)||404,statusText:i&&(i.statusText||i.statusMessage)||`Page Not Found: ${e.fullPath}`,data:{path:e.fullPath}});return typeof window<"u"&&window.history.pushState({},"",t.fullPath),o},Bw=e=>{const t=wp({path:e.path});if(t.redirect){const r=t.redirect.includes("#")?t.redirect:t.redirect+e.hash;return qr(r,{acceptRelative:!0})?(window.location.href=r,!1):r}},jw=[Hw,Bw],Tu={};function qw(e,t,r){const{pathname:n,search:i,hash:o}=t,s=e.indexOf("#");if(s>-1){const c=o.includes(e.slice(s))?e.slice(s).length:1;let l=o.slice(c);return l[0]!=="/"&&(l="/"+l),Kc(zc(l,""))}const a=Kc(zc(n,e)),u=!r||gE(a,r)?a:r;return u+(u.includes("?")?"":i)+o}const Vw=rr({name:"nuxt:router",enforce:"pre",async setup(e){let t,r,n=ro().app.baseURL;const i=Sr.history?.(n)??ew(n),o=Sr.routes?([t,r]=Oi(()=>Sr.routes(ba)),t=await t,r(),t??ba):ba;let s;const a=_w({...Sr,scrollBehavior:(b,E,m)=>{if(E===Pt){s=m;return}if(Sr.scrollBehavior){if(a.options.scrollBehavior=Sr.scrollBehavior,"scrollRestoration"in window.history){const g=a.beforeEach(()=>{g(),window.history.scrollRestoration="manual"})}return Sr.scrollBehavior(b,Pt,s||m)}},history:i,routes:o});"scrollRestoration"in window.history&&(window.history.scrollRestoration="auto"),e.vueApp.use(a);const u=br(a.currentRoute.value);a.afterEach((b,E)=>{u.value=E}),Object.defineProperty(e.vueApp.config.globalProperties,"previousRoute",{get:()=>u.value});const c=qw(n,window.location,e.payload.path),l=br(a.currentRoute.value),f=()=>{l.value=a.currentRoute.value};a.afterEach((b,E)=>{b.matched.at(-1)?.components?.default===E.matched.at(-1)?.components?.default&&f()});const d={sync:f};for(const b in l.value)Object.defineProperty(d,b,{get:()=>l.value[b],enumerable:!0});e._route=hr(d),e._middleware||={global:[],named:{}};const h=Us();a.afterEach(async(b,E,m)=>{delete e._processingMiddleware,!e.isHydrating&&h.value&&await e.runWithContext(d_),m&&await e.callHook("page:loading:end")});try{[t,r]=Oi(()=>a.isReady()),await t,r()}catch(b){[t,r]=Oi(()=>e.runWithContext(()=>Yr(b))),await t,r()}const v=c!==a.currentRoute.value.fullPath?a.resolve(c):a.currentRoute.value;f();const p=e.payload.state._layout;return a.beforeEach(async(b,E)=>{await e.callHook("page:loading:start"),b.meta=Br(b.meta),e.isHydrating&&p&&!mr(b.meta.layout)&&(b.meta.layout=p),e._processingMiddleware=!0;{const m=new Set([...jw,...e._middleware.global]);for(const y of b.matched){const _=y.meta.middleware;if(_)for(const S of xl(_))m.add(S)}const g=wp({path:b.path});if(g.appMiddleware)for(const y in g.appMiddleware)g.appMiddleware[y]?m.add(y):m.delete(y);for(const y of m){const _=typeof y=="string"?e._middleware.named[y]||await Tu[y]?.().then(S=>S.default||S):y;if(!_)throw new Error(`Unknown route middleware: '${y}'.`);try{const S=await e.runWithContext(()=>_(b,E));if(!e.payload.serverRendered&&e.isHydrating&&(S===!1||S instanceof Error)){const A=S||an({status:404,statusText:`Page Not Found: ${c}`});return await e.runWithContext(()=>Yr(A)),!1}if(S===!0)continue;if(S===!1)return S;if(S)return gp(S)&&S.fatal&&await e.runWithContext(()=>Yr(S)),S}catch(S){const A=an(S);return A.fatal&&await e.runWithContext(()=>Yr(A)),A}}}}),a.onError(async()=>{delete e._processingMiddleware,await e.callHook("page:loading:end")}),a.afterEach(b=>{if(b.matched.length===0)return e.runWithContext(()=>Yr(an({status:404,fatal:!1,statusText:`Page not found: ${b.fullPath}`,data:{path:b.fullPath}})))}),e.hooks.hookOnce("app:created",async()=>{try{"name"in v&&(v.name=void 0),await a.replace({...v,force:!0}),a.options.scrollBehavior=Sr.scrollBehavior}catch(b){await e.runWithContext(()=>Yr(b))}}),{provide:{router:a}}}}),Au=globalThis.requestIdleCallback||(e=>{const t=Date.now(),r={didTimeout:!1,timeRemaining:()=>Math.max(0,50-(Date.now()-t))};return setTimeout(()=>{e(r)},1)}),$w=globalThis.cancelIdleCallback||(e=>{clearTimeout(e)}),Hs=e=>{const t=He();t.isHydrating?t.hooks.hookOnce("app:suspense:resolve",()=>{Au(()=>e())}):Au(()=>e())},Ww=rr({name:"nuxt:payload",setup(e){const t=new Set;wt().beforeResolve(async(r,n)=>{if(r.path===n.path)return;const i=await cf(r.path);if(i){for(const o of t)delete e.static.data[o];for(const o in i.data)o in e.static.data||t.add(o),e.static.data[o]=i.data[o]}}),Hs(()=>{e.hooks.hook("link:prefetch",async r=>{const{hostname:n}=new URL(r,window.location.href);n===window.location.hostname&&await cf().catch(()=>{console.warn("[nuxt] Error preloading payload for",r)})}),navigator.connection?.effectiveType!=="slow-2g"&&setTimeout(Sp,1e3)})}}),Gw=rr(()=>{const e=wt();Hs(()=>{e.beforeResolve(async()=>{await new Promise(t=>{setTimeout(t,100),requestAnimationFrame(()=>{setTimeout(t,0)})})})})}),Kw=rr(e=>{let t;async function r(){let n;try{n=await Sp()}catch(i){const o=i;if(!("status"in o&&(o.status===404||o.status===403)))throw o}t&&clearTimeout(t),t=setTimeout(r,ef);try{const i=await $fetch(Il("builds/latest.json")+`?${Date.now()}`);i.id!==n?.id&&(e.hooks.callHook("app:manifest:update",i),t&&clearTimeout(t))}catch{}}Hs(()=>{t=setTimeout(r,ef)})});function zw(e={}){const t=e.path||window.location.pathname;let r={};try{r=ds(sessionStorage.getItem("nuxt:reload")||"{}")}catch{}if(e.force||r?.path!==t||r?.expires{n.clear()}),e.hook("app:chunkError",({error:o})=>{n.add(o)});function i(o){const s=Al(r.app.baseURL,o.fullPath);zw({path:s,persistState:!0})}e.hook("app:manifest:update",()=>{t.beforeResolve(i)}),t.onError((o,s)=>{n.has(o)&&i(s)})}}),Yw=rr({name:"nuxt:global-components"}),bo={};function Jw(e){if(e?.__asyncLoader&&!e.__asyncResolved)return e.__asyncLoader()}async function Bp(e,t=wt()){const{path:r,matched:n}=t.resolve(e);if(!n.length||(t._routePreloaded||=new Set,t._routePreloaded.has(r)))return;const i=t._preloadPromises||=[];if(i.length>4)return Promise.all(i).then(()=>Bp(e,t));t._routePreloaded.add(r);for(const o of n){const s=o.components?.default;if(typeof s!="function")continue;const a=Promise.resolve(s()).catch(()=>{}).finally(()=>i.splice(i.indexOf(a)));i.push(a)}await Promise.all(i)}const Qw=rr({name:"nuxt:prefetch",setup(e){const t=wt();e.hooks.hook("app:mounted",()=>{t.beforeEach(async r=>{const n=r?.meta?.layout;n&&typeof bo[n]=="function"&&await bo[n]()})}),e.hooks.hook("link:prefetch",r=>{if(qr(r))return;const n=t.resolve(r);if(!n)return;const i=n.meta.layout;let o=xl(n.meta.middleware);o=o.filter(s=>typeof s=="string");for(const s of o)typeof Tu[s]=="function"&&Tu[s]();typeof i=="string"&&i in bo&&Jw(bo[i])})}});var Cu=function(e,t){return Cu=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Cu(e,t)};function Dt(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Cu(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var D=function(){return D=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function oe(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function Ee(e,t,r){if(r||arguments.length===2)for(var n=0,i=t.length,o;naT)return!1;for(var t in e){var r=e[t];if(!Wp(t,r))return!1}return!0},Wp=function(e,t){var r,n;if(typeof e!="string")return!1;if(Array.isArray(t)){var i=!0;try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;if(Array.isArray(a))return!1;if(typeof a=="object")i=i&&Iu(a);else if(!["number","string"].includes(typeof a))return!1;if(!i)return!1}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}}else{if(t==null)return!1;if(typeof t=="object")return Iu(t);if(!["number","string","boolean"].includes(typeof t))return!1}return!0},Fn=(function(){function e(){this._propertySet=new Set,this._properties={}}return e.prototype.getUserProperties=function(){return D({},this._properties)},e.prototype.set=function(t,r){return this._safeSet(Me.SET,t,r),this},e.prototype.setOnce=function(t,r){return this._safeSet(Me.SET_ONCE,t,r),this},e.prototype.append=function(t,r){return this._safeSet(Me.APPEND,t,r),this},e.prototype.prepend=function(t,r){return this._safeSet(Me.PREPEND,t,r),this},e.prototype.postInsert=function(t,r){return this._safeSet(Me.POSTINSERT,t,r),this},e.prototype.preInsert=function(t,r){return this._safeSet(Me.PREINSERT,t,r),this},e.prototype.remove=function(t,r){return this._safeSet(Me.REMOVE,t,r),this},e.prototype.add=function(t,r){return this._safeSet(Me.ADD,t,r),this},e.prototype.unset=function(t){return this._safeSet(Me.UNSET,t,Rf),this},e.prototype.clearAll=function(){return this._properties={},this._properties[Me.CLEAR_ALL]=Rf,this},e.prototype._safeSet=function(t,r,n){if(this._validate(t,r,n)){var i=this._properties[t];return i===void 0&&(i={},this._properties[t]=i),i[r]=n,this._propertySet.add(r),!0}return!1},e.prototype._validate=function(t,r,n){return this._properties[Me.CLEAR_ALL]!==void 0||this._propertySet.has(r)?!1:t===Me.ADD?typeof n=="number":t!==Me.UNSET&&t!==Me.REMOVE?Wp(r,n):!0},e})(),Me;(function(e){e.SET="$set",e.SET_ONCE="$setOnce",e.ADD="$add",e.APPEND="$append",e.PREPEND="$prepend",e.REMOVE="$remove",e.PREINSERT="$preInsert",e.POSTINSERT="$postInsert",e.UNSET="$unset",e.CLEAR_ALL="$clearAll"})(Me||(Me={}));var uT=[Me.CLEAR_ALL,Me.UNSET,Me.SET,Me.SET_ONCE,Me.ADD,Me.APPEND,Me.PREPEND,Me.PREINSERT,Me.POSTINSERT,Me.REMOVE],lT="Event tracked successfully",cT="Unexpected error occurred",fT="Event rejected due to exceeded retry count",dT="Event skipped due to optOut config",hT="Event rejected due to missing API key",vT="Invalid API key",pT="Client not initialized",qe;(function(e){e.Unknown="unknown",e.Skipped="skipped",e.Success="success",e.RateLimit="rate_limit",e.PayloadTooLarge="payload_too_large",e.Invalid="invalid",e.Failed="failed",e.Timeout="Timeout",e.SystemError="SystemError"})(qe||(qe={}));var Hn=function(e,t,r){return t===void 0&&(t=0),r===void 0&&(r=qe.Unknown),{event:e,code:t,message:r}},le=function(){var e="ampIntegrationContext";if(typeof globalThis<"u"&&typeof globalThis[e]<"u")return globalThis[e];if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},gT=function(e){return e?(e^Math.random()*16>>e/4).toString(16):(String(1e7)+String(-1e3)+String(-4e3)+String(-8e3)+String(-1e11)).replace(/[018]/g,Zt)},qf=Ee([],oe(Array(256).keys()),!1).map(function(e){return e.toString(16).padStart(2,"0")}),Zt=function(e){var t,r=le();if(!(!((t=r?.crypto)===null||t===void 0)&&t.getRandomValues))return gT(e);var n=r.crypto.getRandomValues(new Uint8Array(16));return n[6]=n[6]&15|64,n[8]=n[8]&63|128,Ee([],oe(n.entries()),!1).map(function(i){var o=oe(i,2),s=o[0],a=o[1];return[4,6,8,10].includes(s)?"-".concat(qf[a]):qf[a]}).join("")},mT=(function(){function e(t){this.client=t,this.queue=[],this.applying=!1,this.plugins=[],this._optOutListeners=[]}return e.prototype.register=function(t,r){var n,i;return x(this,void 0,void 0,function(){return O(this,function(o){switch(o.label){case 0:return this.plugins.some(function(s){return s.name===t.name})?(this.loggerProvider.warn("Plugin with name ".concat(t.name," already exists, skipping registration")),[2]):(t.name===void 0&&(t.name=Zt(),this.loggerProvider.warn(`Plugin name is undefined. - Generating a random UUID for plugin name: `.concat(t.name,`. - Set a name for the plugin to prevent it from being added multiple times.`))),t.type=(n=t.type)!==null&&n!==void 0?n:"enrichment",[4,(i=t.setup)===null||i===void 0?void 0:i.call(t,r,this.client)]);case 1:return o.sent(),this.plugins.push(t),[2]}})})},e.prototype.deregister=function(t,r){var n;return x(this,void 0,void 0,function(){var i,o;return O(this,function(s){switch(s.label){case 0:return i=this.plugins.findIndex(function(a){return a.name===t}),i===-1?(r.loggerProvider.warn("Plugin with name ".concat(t," does not exist, skipping deregistration")),[2]):(o=this.plugins[i],this.plugins.splice(i,1),[4,(n=o.teardown)===null||n===void 0?void 0:n.call(o)]);case 1:return s.sent(),[2]}})})},e.prototype.reset=function(t){this._clearOptOutListeners(),this.applying=!1;var r=this.plugins;r.map(function(n){var i;return(i=n.teardown)===null||i===void 0?void 0:i.call(n)}),this.plugins=[],this.client=t},e.prototype.push=function(t){var r=this;return new Promise(function(n){r.queue.push([t,n]),r.scheduleApply(0)})},e.prototype.scheduleApply=function(t){var r=this;this.applying||(this.applying=!0,setTimeout(function(){r.apply(r.queue.shift()).then(function(){r.applying=!1,r.queue.length>0&&r.scheduleApply(0)})},t))},e.prototype.apply=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u,h,v,c,l,f,d,h,v,p,b,E,m,g,y,_;return O(this,function(S){switch(S.label){case 0:if(!t)return[2];r=oe(t,1),n=r[0],i=oe(t,2),o=i[1],this.loggerProvider.log("Timeline.apply: Initial event",n),s=this.plugins.filter(function(A){return A.type==="before"}),S.label=1;case 1:S.trys.push([1,6,7,8]),a=ce(s),u=a.next(),S.label=2;case 2:return u.done?[3,5]:(h=u.value,h.execute?[4,h.execute(D({},n))]:[3,4]);case 3:if(v=S.sent(),v===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by before plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=v,this.loggerProvider.log("Timeline.apply: Event after before plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),S.label=4;case 4:return u=a.next(),[3,2];case 5:return[3,8];case 6:return c=S.sent(),m={error:c},[3,8];case 7:try{u&&!u.done&&(g=a.return)&&g.call(a)}finally{if(m)throw m.error}return[7];case 8:l=this.plugins.filter(function(A){return A.type==="enrichment"||A.type===void 0}),S.label=9;case 9:S.trys.push([9,14,15,16]),f=ce(l),d=f.next(),S.label=10;case 10:return d.done?[3,13]:(h=d.value,h.execute?[4,h.execute(D({},n))]:[3,12]);case 11:if(v=S.sent(),v===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by enrichment plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=v,this.loggerProvider.log("Timeline.apply: Event after enrichment plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),S.label=12;case 12:return d=f.next(),[3,10];case 13:return[3,16];case 14:return p=S.sent(),y={error:p},[3,16];case 15:try{d&&!d.done&&(_=f.return)&&_.call(f)}finally{if(y)throw y.error}return[7];case 16:return b=this.plugins.filter(function(A){return A.type==="destination"}),this.loggerProvider.log("Timeline.apply: Final event before destinations, event: ".concat(JSON.stringify(n))),E=b.map(function(A){var C=D({},n);return A.execute(C).catch(function(I){return Hn(C,0,String(I))})}),Promise.all(E).then(function(A){var C=oe(A,1),I=C[0],P=I||Hn(n,100,"Event not tracked, no destination plugins on the instance");o(P)}),[2]}})})},e.prototype.flush=function(){return x(this,void 0,void 0,function(){var t,r,n,i=this;return O(this,function(o){switch(o.label){case 0:return t=this.queue,this.queue=[],[4,Promise.all(t.map(function(s){return i.apply(s)}))];case 1:return o.sent(),r=this.plugins.filter(function(s){return s.type==="destination"}),n=r.map(function(s){return s.flush&&s.flush()}),[4,Promise.all(n)];case 2:return o.sent(),[2]}})})},e.prototype.addOptOutListener=function(t){this._optOutListeners.push(t)},e.prototype._clearOptOutListeners=function(){this._optOutListeners=[]},e.prototype.onIdentityChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onIdentityChanged)===null||n===void 0||n.call(r,t)})},e.prototype.onSessionIdChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onSessionIdChanged)===null||n===void 0||n.call(r,t)})},e.prototype.onOptOutChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onOptOutChanged)===null||n===void 0||n.call(r,t)}),this._callOptOutListeners(t)},e.prototype._callOptOutListeners=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u;return O(this,function(c){switch(c.label){case 0:c.trys.push([0,7,8,9]),r=ce(this._optOutListeners),n=r.next(),c.label=1;case 1:if(n.done)return[3,6];i=n.value,c.label=2;case 2:return c.trys.push([2,4,,5]),[4,i(t)];case 3:return c.sent(),[3,5];case 4:return o=c.sent(),this.loggerProvider.error("Error calling optOut listener",o),[3,5];case 5:return n=r.next(),[3,1];case 6:return[3,9];case 7:return s=c.sent(),a={error:s},[3,9];case 8:try{n&&!n.done&&(u=r.return)&&u.call(r)}finally{if(a)throw a.error}return[7];case 9:return[2]}})})},e.prototype.onReset=function(){this.plugins.forEach(function(t){var r;(r=t.onReset)===null||r===void 0||r.call(t)})},e})(),yT=function(e,t,r){var n=typeof e=="string"?{event_type:e}:e;return D(D(D({},n),r),t&&{event_properties:t})},Ll=function(e,t){var r=D(D({},t),{event_type:St.IDENTIFY,user_properties:e.getUserProperties()});return r},bT=function(e,t,r,n){var i,o=D(D({},n),{event_type:St.GROUP_IDENTIFY,group_properties:r.getUserProperties(),groups:(i={},i[e]=t,i)});return o},ET=function(e,t,r){var n,i=new Fn;i.set(e,t);var o=D(D({},r),{event_type:St.IDENTIFY,user_properties:i.getUserProperties(),groups:(n={},n[e]=t,n)});return o},_T=function(e,t){return D(D({},t),{event_type:St.REVENUE,event_properties:e.getEventProperties()})},rt=function(e){return{promise:e||Promise.resolve()}},ST=(function(){function e(t){t===void 0&&(t="$default"),this.initializing=!1,this.isReady=!1,this.q=[],this.dispatchQ=[],this.logEvent=this.track.bind(this),this.timeline=new mT(this),this.name=t}return e.prototype._init=function(t){return x(this,void 0,void 0,function(){return O(this,function(r){switch(r.label){case 0:return this.config=t,this.timeline.reset(this),this.timeline.loggerProvider=this.config.loggerProvider,[4,this.runQueuedFunctions("q")];case 1:return r.sent(),this.isReady=!0,[2]}})})},e.prototype.runQueuedFunctions=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:r=this[t],this[t]=[],l.label=1;case 1:l.trys.push([1,8,9,10]),n=ce(r),i=n.next(),l.label=2;case 2:return i.done?[3,7]:(o=i.value,s=o(),s&&"promise"in s?[4,s.promise]:[3,4]);case 3:return l.sent(),[3,6];case 4:return[4,s];case 5:l.sent(),l.label=6;case 6:return i=n.next(),[3,2];case 7:return[3,10];case 8:return a=l.sent(),u={error:a},[3,10];case 9:try{i&&!i.done&&(c=n.return)&&c.call(n)}finally{if(u)throw u.error}return[7];case 10:return this[t].length?[4,this.runQueuedFunctions(t)]:[3,12];case 11:l.sent(),l.label=12;case 12:return[2]}})})},e.prototype.track=function(t,r,n){var i=yT(t,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),rt(this.dispatch(i))},e.prototype.identify=function(t,r){var n=Ll(t,r);return this.userProperties=this.getOperationAppliedUserProperties(n.user_properties),rt(this.dispatch(n))},e.prototype.groupIdentify=function(t,r,n,i){var o=bT(t,r,n,i);return rt(this.dispatch(o))},e.prototype.setGroup=function(t,r,n){var i=ET(t,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),rt(this.dispatch(i))},e.prototype.revenue=function(t,r){var n=_T(t,r);return rt(this.dispatch(n))},e.prototype.add=function(t){return this.isReady?this._addPlugin(t):(this.q.push(this._addPlugin.bind(this,t)),rt())},e.prototype._addPlugin=function(t){return rt(this.timeline.register(t,this.config))},e.prototype.remove=function(t){return this.isReady?this._removePlugin(t):(this.q.push(this._removePlugin.bind(this,t)),rt())},e.prototype._removePlugin=function(t){return rt(this.timeline.deregister(t,this.config))},e.prototype.dispatchWithCallback=function(t,r){if(!this.isReady)return r(Hn(t,0,pT));this.process(t).then(r)},e.prototype.dispatch=function(t){return x(this,void 0,void 0,function(){var r=this;return O(this,function(n){return this.isReady?[2,this.process(t)]:[2,new Promise(function(i){r.dispatchQ.push(r.dispatchWithCallback.bind(r,t,i))})]})})},e.prototype.getOperationAppliedUserProperties=function(t){var r,n=(r=this.userProperties)!==null&&r!==void 0?r:{},i=D({},n);if(t===void 0)return i;var o={};return Object.keys(t).forEach(function(s){Object.values(vr).includes(s)||(o[s]=t[s])}),uT.forEach(function(s){if(Object.keys(t).includes(s)){var a=t[s];switch(s){case vr.CLEAR_ALL:Object.keys(i).forEach(function(u){delete i[u]});break;case vr.UNSET:Object.keys(a).forEach(function(u){delete i[u]});break;case vr.SET:Object.assign(i,a);break}}}),Object.assign(i,o),i},e.prototype.process=function(t){return x(this,void 0,void 0,function(){var i,r,n,i;return O(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),this.config.optOut?[2,Hn(t,0,dT)]:(t.event_type===St.IDENTIFY&&this.timeline.onIdentityChanged({userProperties:this.userProperties}),[4,this.timeline.push(t)]);case 1:return i=o.sent(),i.code===200?this.config.loggerProvider.log(i.message):i.code===100?this.config.loggerProvider.warn(i.message):this.config.loggerProvider.error(i.message),[2,i];case 2:return r=o.sent(),n=String(r),this.config.loggerProvider.error(n),i=Hn(t,0,n),[2,i];case 3:return[2]}})})},e.prototype.setOptOut=function(t){if(!this.isReady){this.q.push(this._setOptOut.bind(this,!!t));return}this._setOptOut(t)},e.prototype._setOptOut=function(t){this.config.optOut!==t&&(this.config.optOut=!!t,this.timeline.onOptOutChanged(t))},e.prototype.flush=function(){return rt(this.timeline.flush())},e.prototype.plugin=function(t){var r=this.timeline.plugins.find(function(n){return n.name===t});if(r===void 0){this.config.loggerProvider.debug("Cannot find plugin with name ".concat(t));return}return r},e.prototype.plugins=function(t){return this.timeline.plugins.filter(function(r){return r instanceof t})},e})(),Gp=(function(){function e(){this.productId="",this.quantity=1,this.price=0}return e.prototype.setProductId=function(t){return this.productId=t,this},e.prototype.setQuantity=function(t){return t>0&&(this.quantity=t),this},e.prototype.setPrice=function(t){return this.price=t,this},e.prototype.setRevenueType=function(t){return this.revenueType=t,this},e.prototype.setCurrency=function(t){return this.currency=t,this},e.prototype.setRevenue=function(t){return this.revenue=t,this},e.prototype.setReceipt=function(t){return this.receipt=t,this},e.prototype.setReceiptSig=function(t){return this.receiptSig=t,this},e.prototype.setEventProperties=function(t){try{var r=JSON.parse(JSON.stringify(t));Iu(r)&&(this.properties=r)}catch{}return this},e.prototype.getEventProperties=function(){var t=this.properties?D({},this.properties):{};return t[Ft.REVENUE_PRODUCT_ID]=this.productId,t[Ft.REVENUE_QUANTITY]=this.quantity,t[Ft.REVENUE_PRICE]=this.price,t[Ft.REVENUE_TYPE]=this.revenueType,t[Ft.REVENUE_CURRENCY]=this.currency,t[Ft.REVENUE]=this.revenue,t[Ft.RECEIPT]=this.receipt,t[Ft.RECEIPT_SIG]=this.receiptSig,t},e})(),Ft;(function(e){e.REVENUE_PRODUCT_ID="$productId",e.REVENUE_QUANTITY="$quantity",e.REVENUE_PRICE="$price",e.REVENUE_TYPE="$revenueType",e.REVENUE_CURRENCY="$currency",e.REVENUE="$revenue",e.RECEIPT="$receipt",e.RECEIPT_SIG="$receiptSig"})(Ft||(Ft={}));var wT=function(e,t){var r=Math.max(t,1);return e.reduce(function(n,i,o){var s=Math.floor(o/r);return n[s]||(n[s]=[]),n[s].push(i),n},[])},at;(function(e){e[e.None=0]="None",e[e.Error=1]="Error",e[e.Warn=2]="Warn",e[e.Verbose=3]="Verbose",e[e.Debug=4]="Debug"})(at||(at={}));var Eo="Amplitude Logger ",Xn=(function(){function e(){this.logLevel=at.None}return e.prototype.disable=function(){this.logLevel=at.None},e.prototype.enable=function(t){t===void 0&&(t=at.Warn),this.logLevel=t},e.prototype.log=function(){for(var t=[],r=0;r=200&&e<300}var jo=function(e){e===void 0&&(e=0);var t=new Error().stack||"";return t.split(` -`).slice(2+e).map(function(r){return r.trim()})},Pe=function(e){return function(){var t=D({},e.config),r=t.loggerProvider,n=t.logLevel;return{logger:r,logLevel:n}}},CT=function(e,t){var r,n;t=t.replace(/\[(\w+)\]/g,".$1"),t=t.replace(/^\./,"");try{for(var i=ce(t.split(".")),o=i.next();!o.done;o=i.next()){var s=o.value;if(s in e)e=e[s];else return}}catch(a){r={error:a}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return e},Re=function(e,t){return function(){var r,n,i={};try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;i[a]=CT(e,a)}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return i}},_e=function(e,t,r,n,i){return i===void 0&&(i=null),function(){for(var o=[],s=0;s0&&Promise.all(n.map(function(s){return i.execute(s)})).catch(),[2,Promise.resolve(void 0)]}})})},e.prototype.execute=function(t){var r=this;return t.insert_id||(t.insert_id=Zt()),new Promise(function(n){var i={event:t,attempts:0,callback:function(o){return n(o)},timeout:0};r.queue.push(i),r.schedule(r.config.flushIntervalMillis),r.saveEvents()})},e.prototype.removeEventsExceedFlushMaxRetries=function(t){var r=this;return t.filter(function(n){return n.attempts+=1,n.attemptsthis.scheduledTimeout)){this.scheduleId&&clearTimeout(this.scheduleId),this.scheduledTimeout=t,this.scheduleId=setTimeout(function(){r.queue=r.queue.map(function(n){return n.timeout=0,n}),r.flush(!0)},t);return}},e.prototype.resetSchedule=function(){this.scheduleId=null,this.scheduledTimeout=0},e.prototype.flush=function(t){return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var r,n,i,o=this;return O(this,function(s){switch(s.label){case 0:return this.config.offline?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush while offline."),[2]):this.flushId?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush because previous flush has not resolved."),[2]):(this.flushId=this.scheduleId,this.resetSchedule(),r=[],n=[],this.queue.forEach(function(a){return a.timeout===0?r.push(a):n.push(a)}),i=wT(r,this.config.flushQueueSize),[4,i.reduce(function(a,u){return x(o,void 0,void 0,function(){return O(this,function(c){switch(c.label){case 0:return[4,a];case 1:return c.sent(),[4,this.send(u,t)];case 2:return[2,c.sent()]}})})},Promise.resolve())]);case 1:return s.sent(),this.flushId=null,this.scheduleEvents(this.queue),[2]}})})},e.prototype.send=function(t,r){var n;return r===void 0&&(r=!0),x(this,void 0,void 0,function(){var i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:if(!this.config.apiKey)return[2,this.fulfillRequest(t,400,hT)];i={api_key:this.config.apiKey,events:t.map(function(f){var d=f.event;d.extra;var h=gs(d,["extra"]);return h}),options:{min_id_length:this.config.minIdLength},client_upload_time:new Date().toISOString(),request_metadata:this.config.requestMetadata},this.config.requestMetadata=new AT,l.label=1;case 1:return l.trys.push([1,3,,4]),o=zp(this.config.serverUrl,this.config.serverZone,this.config.useBatch).serverUrl,s=kT(o,this.config.enableRequestBodyCompression),[4,this.config.transportProvider.send(o,i,this.config._enableRequestBodyCompressionExperimental&&s)];case 2:return a=l.sent(),a===null?(this.fulfillRequest(t,0,cT),[2]):r?(this.handleResponse(a,t),[3,4]):("body"in a?this.fulfillRequest(t,a.statusCode,"".concat(a.status,": ").concat(li(a))):this.fulfillRequest(t,a.statusCode,a.status),[2]);case 3:return u=l.sent(),c=PT(u),this.config.loggerProvider.error(c),(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful.from.catch.error",{events:t.map(function(f){return f.event.event_type}),message:c,stack_trace:jo()}),this.handleResponse({status:qe.Failed,statusCode:0},t),[3,4];case 4:return[2]}})})},e.prototype.handleResponse=function(t,r){var n;ku(t.statusCode)||(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful",{events:r.map(function(o){return o.event.event_type}),code:t.statusCode,status:t.status,body:li(t),stack_trace:jo()});var i=t.status;switch(i){case qe.Success:{this.handleSuccessResponse(t,r);break}case qe.Invalid:{this.handleInvalidResponse(t,r);break}case qe.PayloadTooLarge:{this.handlePayloadTooLargeResponse(t,r);break}case qe.RateLimit:{this.handleRateLimitResponse(t,r);break}default:{this.config.loggerProvider.warn(`{code: 0, error: "Status '`.concat(i,"' provided for ").concat(r.length,' events"}')),this.handleOtherResponse(r);break}}},e.prototype.handleSuccessResponse=function(t,r){this.fulfillRequest(r,t.statusCode,lT)},e.prototype.handleInvalidResponse=function(t,r){var n=this;if(t.body.missingField||t.body.error.startsWith(vT)){this.fulfillRequest(r,t.statusCode,t.body.error);return}var i=Ee(Ee(Ee(Ee([],oe(Object.values(t.body.eventsWithInvalidFields)),!1),oe(Object.values(t.body.eventsWithMissingFields)),!1),oe(Object.values(t.body.eventsWithInvalidIdLengths)),!1),oe(t.body.silencedEvents),!1).flat(),o=new Set(i),s=r.filter(function(u,c){if(o.has(c)){n.fulfillRequest([u],t.statusCode,t.body.error);return}return!0});s.length>0&&this.config.loggerProvider.warn(li(t));var a=this.removeEventsExceedFlushMaxRetries(s);this.scheduleEvents(a)},e.prototype.handlePayloadTooLargeResponse=function(t,r){if(r.length===1){this.fulfillRequest(r,t.statusCode,t.body.error);return}this.config.loggerProvider.warn(li(t)),this.config.flushQueueSize/=2;var n=this.removeEventsExceedFlushMaxRetries(r);this.scheduleEvents(n)},e.prototype.handleRateLimitResponse=function(t,r){var n=this,i=Object.keys(t.body.exceededDailyQuotaUsers),o=Object.keys(t.body.exceededDailyQuotaDevices),s=t.body.throttledEvents,a=new Set(i),u=new Set(o),c=new Set(s),l=r.filter(function(d,h){if(d.event.user_id&&a.has(d.event.user_id)||d.event.device_id&&u.has(d.event.device_id)){n.fulfillRequest([d],t.statusCode,t.body.error);return}return c.has(h)&&(d.timeout=n.throttleTimeout),!0});l.length>0&&this.config.loggerProvider.warn(li(t));var f=this.removeEventsExceedFlushMaxRetries(l);this.scheduleEvents(f)},e.prototype.handleOtherResponse=function(t){var r=this,n=t.map(function(o){return o.timeout=o.attempts*r.retryTimeout,o}),i=this.removeEventsExceedFlushMaxRetries(n);this.scheduleEvents(i)},e.prototype.fulfillRequest=function(t,r,n){var i,o,s;ku(r)?(s=this.diagnosticsClient)===null||s===void 0||s.increment("analytics.events.sent",t.length):((i=this.diagnosticsClient)===null||i===void 0||i.increment("analytics.events.dropped",t.length),(o=this.diagnosticsClient)===null||o===void 0||o.recordEvent("analytics.events.dropped",{events:t.map(function(a){return a.event.event_type}),code:r,message:n,stack_trace:jo()})),this.removeEvents(t),t.forEach(function(a){return a.callback(Hn(a.event,r,n))})},e.prototype.saveEvents=function(){if(this.config.storageProvider){var t=this.queue.map(function(r){return r.event});this.config.storageProvider.set(this.storageKey,t)}},e.prototype.removeEvents=function(t){this.queue=this.queue.filter(function(r){return!t.some(function(n){return n.event.insert_id===r.event.insert_id})}),this.saveEvents()},e})(),OT=(function(){function e(){}return e.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:xT(),platform:"Web",os:void 0,deviceModel:void 0}},e})(),xT=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},NT=(function(){function e(){this.queue=[]}return e.prototype.logEvent=function(t){this.receiver?this.receiver(t):this.queue.length<512&&this.queue.push(t)},e.prototype.setEventReceiver=function(t){this.receiver=t,this.queue.length>0&&(this.queue.forEach(function(r){t(r)}),this.queue=[])},e})(),kr=function(){return kr=Object.assign||function(t){for(var r,n=1,i=arguments.length;n=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Vf(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}var Vo=function(e,t){var r,n,i=["string","number","boolean","undefined"],o=typeof e,s=typeof t;if(o!==s)return!1;try{for(var a=qo(i),u=a.next();!u.done;u=a.next()){var c=u.value;if(c===o)return e===t}}catch(b){r={error:b}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}if(e==null&&t==null)return!0;if(e==null||t==null||e.length!==t.length)return!1;var l=Array.isArray(e),f=Array.isArray(t);if(l!==f)return!1;if(l&&f){for(var d=0;de},Yp=function(e,t,r){return t===void 0&&(t=""),r===void 0&&(r=10),[Bs,t,e.substring(0,r)].filter(Boolean).join("_")},jT=function(e){return"".concat(Bs.toLowerCase(),"_").concat(e.substring(0,6))},qT=function(){var e,t,r,n;if(typeof navigator>"u")return"";var i=navigator.userLanguage;return(n=(r=(t=(e=navigator.languages)===null||e===void 0?void 0:e[0])!==null&&t!==void 0?t:navigator.language)!==null&&r!==void 0?r:i)!==null&&n!==void 0?n:""},ys=function(){var e,t=le();if(!(!((e=t?.location)===null||e===void 0)&&e.search))return{};var r=t.location.search.substring(1).split("&").filter(Boolean),n=r.reduce(function(i,o){var s=o.split("=",2),a=$f(s[0]),u=$f(s[1]);return u&&(i[a]=u),i},{});return n},$f=function(e){e===void 0&&(e="");try{return decodeURIComponent(e)}catch{return""}},Pu=function(e,t){return!t||!t.length?!0:t.some(function(r){return typeof r=="string"?e===r:e.match(r)})},Lr=function(e,t){var r=e;try{r=decodeURI(e)}catch(n){t?.error("Malformed URI sequence: ",n)}return r},Ru=function(e){var t=0;if(e.length===0)return t;for(var r=0;r1&&((r=this.config.diagnosticsClient)===null||r===void 0||r.recordEvent("cookies.duplicate",{cookies:a.map(function(v){return v.domain})}),(n=this.config.diagnosticsClient)===null||n===void 0||n.increment("cookies.duplicate.occurrence.cookieStore"));try{for(u=ce(a),c=u.next();!c.done;c=u.next())if(l=c.value,Qp(l.domain,this.options.domain))return[2,l.value]}catch(v){f={error:v}}finally{try{c&&!c.done&&(d=u.return)&&d.call(u)}finally{if(f)throw f.error}}}h.label=3;case 3:return[3,5];case 4:return h.sent(),[3,5];case 5:return[2,this.getRawSync(t)]}})})},e.prototype.getRawSync=function(t){var r=this,n,i,o=le(),s=((i=(n=o?.document)===null||n===void 0?void 0:n.cookie.split("; "))!==null&&i!==void 0?i:[]).filter(function(c){return c.indexOf(t+"=")===0}),a=void 0,u=this.config.duplicateResolverFn;if(typeof u=="function"&&s.length>1&&(a=s.find(function(c){var l;try{var f=u(c.substring(t.length+1));return f||(l=r.config.diagnosticsClient)===null||l===void 0||l.increment("cookies.duplicate.occurrence.document.cookie"),f}catch{return!1}})),a||(a=s[0]),!!a)return a.substring(t.length+1)},e.prototype.set=function(t,r){return x(this,void 0,void 0,function(){return O(this,function(n){return this.setSync(t,r),[2]})})},e.prototype.setSync=function(t,r){var n;try{var i=(n=this.options.expirationDays)!==null&&n!==void 0?n:0,o=r!==null?i:-1,s=void 0;if(o){var a=new Date;a.setTime(a.getTime()+o*24*60*60*1e3),s=a}var u="".concat(t,"=").concat(btoa(encodeURIComponent(JSON.stringify(r))));s&&(u+="; expires=".concat(s.toUTCString())),u+="; path=/",this.options.domain&&(u+="; domain=".concat(this.options.domain)),this.options.secure&&(u+="; Secure"),this.options.sameSite&&(u+="; SameSite=".concat(this.options.sameSite));var c=le();c?.document&&(c.document.cookie=u)}catch(f){var l=f instanceof Error?f.message:String(f);console.error("Amplitude Logger [Error]: Failed to set cookie for key: ".concat(t,". Error: ").concat(l))}},e.prototype.remove=function(t){return x(this,void 0,void 0,function(){return O(this,function(r){switch(r.label){case 0:return[4,this.set(t,null)];case 1:return r.sent(),[2]}})})},e.prototype.reset=function(){return x(this,void 0,void 0,function(){return O(this,function(t){return[2]})})},e.isDomainWritable=function(t){return x(this,void 0,void 0,function(){var r,n,i,o;return O(this,function(s){switch(s.label){case 0:if(e.cachedTlds[t])return[2,!0];r={domain:"."+t},n="AMP_TLDTEST",i=new e(r),s.label=1;case 1:return s.trys.push([1,3,,4]),[4,i.transaction(n,function(a){if(e.cachedTlds[t])return!0;try{a.set(1);var u=!!a.get();return u&&(e.cachedTlds[t]=!0),u}finally{a.set(null)}})];case 2:return o=s.sent(),[2,!!o];case 3:return s.sent(),[2,!1];case 4:return[2]}})})},e.prototype.transaction=function(t,r){return x(this,void 0,void 0,function(){var n,i,o=this;return O(this,function(s){switch(s.label){case 0:if(n=VT(),i=function(){var a={get:function(){return o.getSync(t)},set:function(u){return o.setSync(t,u)}};return r(a)},!n)return[2,i()];s.label=1;case 1:return s.trys.push([1,3,,4]),[4,n.request("com.amplitude:cookie-lock:".concat(t),i)];case 2:return[2,s.sent()];case 3:return s.sent(),[2,i()];case 4:return[2]}})})},e.cachedTlds={},e})(),$T=function(e){try{return decodeURIComponent(atob(e))}catch{return}},WT=function(e){try{return decodeURIComponent(atob(decodeURIComponent(e)))}catch{return}},Jp=function(e){var t;return(t=$T(e))!==null&&t!==void 0?t:WT(e)},Qp=function(e,t){if(e===""&&t==="")return!0;if(!e||!t)return!1;var r=e.startsWith(".")?e.substring(1):e,n=t.startsWith(".")?t.substring(1):t;return r.toLowerCase()===n.toLowerCase()},Gf=function(e,t,r){return t===void 0&&(t=""),r===void 0&&(r=10),[Bs,t,e.substring(0,r)].filter(Boolean).join("_")},qs=(function(){function e(t){this.storage=t}return e.prototype.isEnabled=function(){return x(this,void 0,void 0,function(){var t,r,n,i;return O(this,function(o){switch(o.label){case 0:if(!this.storage)return[2,!1];t=String(Date.now()),r=new e(this.storage),n="AMP_TEST",o.label=1;case 1:return o.trys.push([1,4,5,7]),[4,r.set(n,t)];case 2:return o.sent(),[4,r.get(n)];case 3:return i=o.sent(),[2,i===t];case 4:return o.sent(),[2,!1];case 5:return[4,r.remove(n)];case 6:return o.sent(),[7];case 7:return[2]}})})},e.prototype.get=function(t){return x(this,void 0,void 0,function(){var r;return O(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,this.getRaw(t)];case 1:return r=n.sent(),r?[2,JSON.parse(r)]:[2,void 0];case 2:return n.sent(),console.error("[Amplitude] Error: Could not get value from storage"),[2,void 0];case 3:return[2]}})})},e.prototype.getRaw=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){return[2,((r=this.storage)===null||r===void 0?void 0:r.getItem(t))||void 0]})})},e.prototype.set=function(t,r){var n;return x(this,void 0,void 0,function(){return O(this,function(i){try{(n=this.storage)===null||n===void 0||n.setItem(t,JSON.stringify(r))}catch{}return[2]})})},e.prototype.remove=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){try{(r=this.storage)===null||r===void 0||r.removeItem(t)}catch{}return[2]})})},e.prototype.reset=function(){var t;return x(this,void 0,void 0,function(){return O(this,function(r){try{(t=this.storage)===null||t===void 0||t.clear()}catch{}return[2]})})},e})(),GT=10,KT=1,we={TAGS:"tags",COUNTERS:"counters",HISTOGRAMS:"histograms",EVENTS:"events",INTERNAL:"internal"},Kf={LAST_FLUSH_TIMESTAMP:"last_flush_timestamp"},zf=(function(){function e(t,r){this.dbPromise=null,this.logger=r,this.dbName="AMP_diagnostics_".concat(t.substring(0,10))}return e.isSupported=function(){var t;return((t=le())===null||t===void 0?void 0:t.indexedDB)!==void 0},e.prototype.getDB=function(){return x(this,void 0,void 0,function(){return O(this,function(t){return this.dbPromise||(this.dbPromise=this.openDB()),[2,this.dbPromise]})})},e.prototype.openDB=function(){var t=this;return new Promise(function(r,n){var i=indexedDB.open(t.dbName,KT);i.onerror=function(){t.dbPromise=null,n(new Error("Failed to open IndexedDB"))},i.onsuccess=function(){var o=i.result;o.onclose=function(){t.dbPromise=null,t.logger.debug("DiagnosticsStorage: DB connection closed.")},o.onerror=function(s){t.logger.debug("DiagnosticsStorage: A global database error occurred.",s),o.close()},r(o)},i.onupgradeneeded=function(o){var s=o.target.result;t.createTables(s)}})},e.prototype.createTables=function(t){if(t.objectStoreNames.contains(we.TAGS)||t.createObjectStore(we.TAGS,{keyPath:"key"}),t.objectStoreNames.contains(we.COUNTERS)||t.createObjectStore(we.COUNTERS,{keyPath:"key"}),t.objectStoreNames.contains(we.HISTOGRAMS)||t.createObjectStore(we.HISTOGRAMS,{keyPath:"key"}),!t.objectStoreNames.contains(we.EVENTS)){var r=t.createObjectStore(we.EVENTS,{keyPath:"id",autoIncrement:!0});r.createIndex("time_idx","time",{unique:!1})}t.objectStoreNames.contains(we.INTERNAL)||t.createObjectStore(we.INTERNAL,{keyPath:"key"})},e.prototype.setTags=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.TAGS],"readwrite"),i=n.objectStore(we.TAGS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to set tags",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.put({key:d,value:h});v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to set tag",d,h,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to set tags",o),[3,3];case 3:return[2]}})})},e.prototype.incrementCounters=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.COUNTERS],"readwrite"),i=n.objectStore(we.COUNTERS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to increment counters",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.get(d);v.onsuccess=function(){var p=v.result,b=p?p.value:0,E=i.put({key:d,value:b+h});E.onerror=function(m){s.logger.debug("DiagnosticsStorage: Failed to update counter",d,m)}},v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to read existing counter",d,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to increment counters",o),[3,3];case 3:return[2]}})})},e.prototype.setHistogramStats=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.HISTOGRAMS],"readwrite"),i=n.objectStore(we.HISTOGRAMS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to set histogram stats",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.get(d);v.onsuccess=function(){var p=v.result,b;p?b={key:d,count:p.count+h.count,min:Math.min(p.min,h.min),max:Math.max(p.max,h.max),sum:p.sum+h.sum}:b={key:d,count:h.count,min:h.min,max:h.max,sum:h.sum};var E=i.put(b);E.onerror=function(m){s.logger.debug("DiagnosticsStorage: Failed to set histogram stats",d,m)}},v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to read existing histogram stats",d,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to set histogram stats",o),[3,3];case 3:return[2]}})})},e.prototype.addEventRecords=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),t.length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.EVENTS],"readwrite"),i=n.objectStore(we.EVENTS),[2,new Promise(function(u){n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to add event records",l),u()};var c=i.count();c.onsuccess=function(){var l=c.result,f=Math.max(0,GT-l);f=_a){this.logger.debug("DiagnosticsClient: Early return setTags as reaching memory limit");return}this.inMemoryTags[t]=r,this.startTimersIfNeeded()}},e.prototype.increment=function(t,r){if(r===void 0&&(r=1),!!this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryCounters).length>=_a){this.logger.debug("DiagnosticsClient: Early return increment as reaching memory limit");return}this.inMemoryCounters[t]=(this.inMemoryCounters[t]||0)+r,this.startTimersIfNeeded()}},e.prototype.recordHistogram=function(t,r){if(this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryHistograms).length>=_a){this.logger.debug("DiagnosticsClient: Early return recordHistogram as reaching memory limit");return}var n=this.inMemoryHistograms[t];n?(n.count+=1,n.min=Math.min(n.min,r),n.max=Math.max(n.max,r),n.sum+=r):this.inMemoryHistograms[t]={count:1,min:r,max:r,sum:r},this.startTimersIfNeeded()}},e.prototype.recordEvent=function(t,r){if(this.isStorageAndTrackEnabled()){if(this.inMemoryEvents.length>=nA){this.logger.debug("DiagnosticsClient: Early return recordEvent as reaching memory limit");return}this.inMemoryEvents.push({event_name:t,time:Date.now(),event_properties:r}),this.startTimersIfNeeded()}},e.prototype.startTimersIfNeeded=function(){var t=this;this.saveTimer||(this.saveTimer=setTimeout(function(){t.saveAllDataToStorage().catch(function(r){t.logger.debug("DiagnosticsClient: Failed to save all data to storage",r)}).finally(function(){t.saveTimer=null})},eA)),this.flushTimer||(this.flushTimer=setTimeout(function(){t._flush().catch(function(r){t.logger.debug("DiagnosticsClient: Failed to flush",r)}).finally(function(){t.flushTimer=null})},_o))},e.prototype.saveAllDataToStorage=function(){return x(this,void 0,void 0,function(){var t,r,n,i;return O(this,function(o){switch(o.label){case 0:return this.storage?(t=D({},this.inMemoryTags),r=D({},this.inMemoryCounters),n=D({},this.inMemoryHistograms),i=Ee([],oe(this.inMemoryEvents),!1),this.inMemoryEvents=[],this.inMemoryTags={},this.inMemoryCounters={},this.inMemoryHistograms={},[4,Promise.all([this.storage.setTags(t),this.storage.incrementCounters(r),this.storage.setHistogramStats(n),this.storage.addEventRecords(i)])]):[2];case 1:return o.sent(),[2]}})})},e.prototype._flush=function(){return x(this,void 0,void 0,function(){var t,r,n,i,o,s,a,u,c,l;return O(this,function(f){switch(f.label){case 0:return this.storage?[4,this.saveAllDataToStorage()]:[2];case 1:return f.sent(),this.saveTimer=null,this.flushTimer=null,[4,this.storage.getAllAndClear()];case 2:return t=f.sent(),r=t.tags,n=t.counters,i=t.histogramStats,o=t.events,this.storage.setLastFlushTimestamp(Date.now()),s={},r.forEach(function(d){s[d.key]=d.value}),a={},n.forEach(function(d){a[d.key]=d.value}),u={},i.forEach(function(d){u[d.key]={count:d.count,min:d.min,max:d.max,avg:Math.round(d.sum/d.count*100)/100}}),c=o.map(function(d){return{event_name:d.event_name,time:d.time,event_properties:d.event_properties}}),Object.keys(a).length===0&&Object.keys(u).length===0&&c.length===0?[2]:(l={tags:s,histogram:u,counters:a,events:c},this.fetch(l),[2])}})})},e.prototype.fetch=function(t){return x(this,void 0,void 0,function(){var r,n;return O(this,function(i){switch(i.label){case 0:if(i.trys.push([0,2,,3]),!le())throw new Error("DiagnosticsClient: Fetch is not supported");return[4,fetch(this.serverUrl,{method:"POST",headers:{"X-ApiKey":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(t)})];case 1:return r=i.sent(),r.ok?(this.logger.debug("DiagnosticsClient: Successfully sent diagnostics data"),[3,3]):(this.logger.debug("DiagnosticsClient: Failed to send diagnostics data."),[2]);case 2:return n=i.sent(),this.logger.debug("DiagnosticsClient: Failed to send diagnostics data. ",n),[3,3];case 3:return[2]}})})},e.prototype.initializeFlushInterval=function(){return x(this,void 0,void 0,function(){var t,r,n;return O(this,function(i){switch(i.label){case 0:return this.storage?(t=Date.now(),[4,this.storage.getLastFlushTimestamp()]):[2];case 1:return r=i.sent()||-1,r===-1?(this.storage.setLastFlushTimestamp(t),this._setFlushTimer(_o),[2]):(n=t-r,n>=_o?(this._flush(),[2]):(this._setFlushTimer(_o-n),[2]))}})})},e.prototype._setFlushTimer=function(t){var r=this;this.flushTimer=setTimeout(function(){r._flush().catch(function(n){r.logger.debug("DiagnosticsClient: Failed to flush",n)}).finally(function(){r.flushTimer=null})},t)},e.prototype._setSampleRate=function(t){this.logger.debug("DiagnosticsClient: Setting sample rate to",t),this.config.sampleRate=t,this.shouldTrack=Wf(this.startTimestamp,this.config.sampleRate)&&this.config.enabled,this.logger.debug("DiagnosticsClient: Should track is",this.shouldTrack)},e})(),no=(function(){function e(){}return e.prototype.send=function(t,r,n){return Promise.resolve(null)},e.prototype.buildResponse=function(t){var r,n,i,o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A;if(typeof t!="object")return null;var C=t.code||0,I=this.buildStatus(C);switch(I){case qe.Success:return{status:I,statusCode:C,body:{eventsIngested:(r=t.events_ingested)!==null&&r!==void 0?r:0,payloadSizeBytes:(n=t.payload_size_bytes)!==null&&n!==void 0?n:0,serverUploadTime:(i=t.server_upload_time)!==null&&i!==void 0?i:0}};case qe.Invalid:return{status:I,statusCode:C,body:{error:(o=t.error)!==null&&o!==void 0?o:"",missingField:(s=t.missing_field)!==null&&s!==void 0?s:"",eventsWithInvalidFields:(a=t.events_with_invalid_fields)!==null&&a!==void 0?a:{},eventsWithMissingFields:(u=t.events_with_missing_fields)!==null&&u!==void 0?u:{},eventsWithInvalidIdLengths:(c=t.events_with_invalid_id_lengths)!==null&&c!==void 0?c:{},epsThreshold:(l=t.eps_threshold)!==null&&l!==void 0?l:0,exceededDailyQuotaDevices:(f=t.exceeded_daily_quota_devices)!==null&&f!==void 0?f:{},silencedDevices:(d=t.silenced_devices)!==null&&d!==void 0?d:[],silencedEvents:(h=t.silenced_events)!==null&&h!==void 0?h:[],throttledDevices:(v=t.throttled_devices)!==null&&v!==void 0?v:{},throttledEvents:(p=t.throttled_events)!==null&&p!==void 0?p:[]}};case qe.PayloadTooLarge:return{status:I,statusCode:C,body:{error:(b=t.error)!==null&&b!==void 0?b:""}};case qe.RateLimit:return{status:I,statusCode:C,body:{error:(E=t.error)!==null&&E!==void 0?E:"",epsThreshold:(m=t.eps_threshold)!==null&&m!==void 0?m:0,throttledDevices:(g=t.throttled_devices)!==null&&g!==void 0?g:{},throttledUsers:(y=t.throttled_users)!==null&&y!==void 0?y:{},exceededDailyQuotaDevices:(_=t.exceeded_daily_quota_devices)!==null&&_!==void 0?_:{},exceededDailyQuotaUsers:(S=t.exceeded_daily_quota_users)!==null&&S!==void 0?S:{},throttledEvents:(A=t.throttled_events)!==null&&A!==void 0?A:[]}};case qe.Timeout:default:return{status:I,statusCode:C}}},e.prototype.buildStatus=function(t){return ku(t)?qe.Success:t===429?qe.RateLimit:t===413?qe.PayloadTooLarge:t===408?qe.Timeout:t>=400&&t<500?qe.Invalid:t>=500?qe.Failed:qe.Unknown},e})(),oA=(function(e){Dt(t,e);function t(r){r===void 0&&(r={});var n=e.call(this)||this;return n.customHeaders=r,n}return t.prototype.send=function(r,n){return x(this,void 0,void 0,function(){var i,o,s;return O(this,function(a){switch(a.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return i={headers:D({"Content-Type":"application/json",Accept:"*/*"},this.customHeaders),body:JSON.stringify(n),method:"POST"},[4,fetch(r,i)];case 1:return o=a.sent(),[4,o.text()];case 2:s=a.sent();try{return[2,this.buildResponse(JSON.parse(s))]}catch{return[2,this.buildResponse({code:o.status})]}return[2]}})})},t})(no),Zp=2*1024;function eg(){return typeof CompressionStream<"u"}function tg(e){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:if(t=CompressionStream,typeof t>"u")return[2,void 0];n.label=1;case 1:return n.trys.push([1,3,,4]),r=new Blob([e]).stream().pipeThrough(new t("gzip")),[4,new Response(r).arrayBuffer()];case 2:return[2,n.sent()];case 3:return n.sent(),[2,void 0];case 4:return[2]}})})}var sA=(function(){function e(t,r){this.key="AMP_remote_config_".concat(t.substring(0,10)),this.logger=r}return e.prototype.fetchConfig=function(){var t=null,r={remoteConfig:null,lastFetch:new Date};try{t=localStorage.getItem(this.key)}catch(i){return this.logger.debug("Remote config localstorage failed to access: ",i),Promise.resolve(r)}if(t===null)return this.logger.debug("Remote config localstorage gets null because the key does not exist"),Promise.resolve(r);try{var n=JSON.parse(t);return this.logger.debug("Remote config localstorage parsed successfully: ".concat(JSON.stringify(n))),Promise.resolve({remoteConfig:n.remoteConfig,lastFetch:new Date(n.lastFetch)})}catch(i){return this.logger.debug("Remote config localstorage failed to parse: ",i),localStorage.removeItem(this.key),Promise.resolve(r)}},e.prototype.setConfig=function(t){try{return localStorage.setItem(this.key,JSON.stringify(t)),this.logger.debug("Remote config localstorage set successfully."),Promise.resolve(!0)}catch(r){this.logger.debug("Remote config localstorage failed to set: ",r)}return Promise.resolve(!1)},e})(),aA="https://sr-client-cfg.amplitude.com/config",uA="https://sr-client-cfg.eu.amplitude.com/config",lA=3,Sa={INVALID_API_KEY:401,FORBIDDEN:403,RATE_LIMIT:429},cA=1e3,fA=300*1e3,rg=(function(){function e(t,r,n,i){n===void 0&&(n="US"),this.callbackInfos=[],this.lastSuccessfulFetch=null,this.fetchPromise=null,this.isLastFetchInvalidApiKey=!1,this.apiKey=t,this.serverUrl=i||(n==="US"?aA:uA),this.logger=r,this.storage=new sA(t,r)}return e.prototype.subscribe=function(t,r,n){var i=Zt(),o={id:i,key:t,deliveryMode:r,callback:n};return this.callbackInfos.push(o),r==="all"?this.subscribeAll(o):this.subscribeWaitForRemote(o,r.timeout),i},e.prototype.unsubscribe=function(t){var r=this.callbackInfos.findIndex(function(n){return n.id===t});return r===-1?(this.logger.debug("Remote config client unsubscribe failed because callback with id ".concat(t," doesn't exist.")),!1):(this.callbackInfos.splice(r,1),this.logger.debug("Remote config client unsubscribe succeeded removing callback with id ".concat(t,".")),!0)},e.prototype.updateConfigs=function(){return x(this,void 0,void 0,function(){var t,r,n=this;return O(this,function(i){switch(i.label){case 0:return this.lastSuccessfulFetch&&(t=Date.now()-this.lastSuccessfulFetch,t=400&&p.status<500&&p.status!==Sa.RATE_LIMIT&&(d=!1),[3,6];case 4:return[4,p.json()];case 5:return E=g.sent(),[2,{value:{remoteConfig:E,lastFetch:new Date}}];case 6:return[3,9];case 7:return m=g.sent(),m instanceof Error&&m.name==="AbortError"?s.logger.debug("Remote config client fetch with retry time ".concat(t," timed out after ").concat(r,"ms")):s.logger.debug("Remote config client fetch with retry time ".concat(t," is rejected because: "),m),[3,9];case 8:return clearTimeout(v),[7];case 9:return d?f=0;s--)if(o[s]===e){o.splice(s,1);break}}catch(a){t={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}}function pA(){var e,t;try{for(var r=ce(Object.entries(Ni)),n=r.next();!n.done;n=r.next()){var i=oe(n.value,2),o=i[0],s=i[1];s&&(Tn[o]=s)}}catch(a){e={error:a}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}Ni={},un={}}var Yf={addListener:hA,removeListener:vA,_restoreConsole:pA},Jn;(function(e){e.US="US",e.EU="EU",e.STAGING="STAGING"})(Jn||(Jn={}));var ng=null,ig=["a","button","input","select","textarea","label","video","audio",'[contenteditable="true" i]',"[data-amp-default-track]",".amp-default-track"],Ul="data-amp-track-",og=["div","span","h1","h2","h3","h4","h5","h6"],sg=150,gA=["a","button",'[role="button"]','[role="link"]','[role="menuitem"]','[role="menuitemcheckbox"]','[role="menuitemradio"]','[role="option"]','[role="tab"]','[role="treeitem"]','[contenteditable="true" i]'],ag=Ee(['input[type="button"]','input[type="submit"]','input[type="reset"]','input[type="image"]','input[type="file"]'],oe(gA),!1),mA=ag,yA=ag,bA=["*"],EA=1e3,_A=4,SA=50;function wA(e){return typeof e=="string"||typeof e=="number"||typeof e=="boolean"||e===null||e===void 0}function ug(e,t,r){if(e){var n=t.map(Jf),i=r.map(Jf);lg({json:e,allowlist:n,excludelist:i,ancestors:[]})}}function lg(e){var t,r,n=e.json,i=e.targetObject,o=e.allowlist,s=e.excludelist,a=e.ancestors,u=e.parentObject,c=e.targetKey;i||(i=n);var l=Object.keys(i);try{for(var f=ce(l),d=f.next();!d.done;d=f.next()){var h=d.value,v=Ee(Ee([],oe(a),!1),[h],!1);wA(i[h])?(!Qf(v,o)||Qf(v,s))&&delete i[h]:lg({json:n,targetObject:i[h],allowlist:o,excludelist:s,ancestors:v,parentObject:i,targetKey:h})}}catch(p){t={error:p}}finally{try{d&&!d.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}Object.keys(i).length===0&&u&&c&&delete u[c]}function Jf(e){return e.startsWith("/")&&(e=e.slice(1)),e.split("/").map(function(t){return t.replace(/~0/g,"~").replace(/~1/g,"/")})}function Ou(e,t,r,n){if(r===void 0&&(r=0),n===void 0&&(n=0),n===t.length)return r===e.length;if(r===e.length){for(;n=t)return}}catch(b){r={error:b}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}i=c}else if(e instanceof ReadableStream){a=e;return}return i}}var IA=(function(){function e(t){this.response=t}return e.prototype.headers=function(t){var r;if(t===void 0&&(t=[]),this.response.headers instanceof Headers){var n=this.response.headers,i={};return(r=n?.forEach)===null||r===void 0||r.call(n,function(o,s){i[s]=o}),Vs(i,{allow:t})}},Object.defineProperty(e.prototype,"bodySize",{get:function(){var t,r;if(this._bodySize!==void 0)return this._bodySize;var n=(r=(t=this.response.headers)===null||t===void 0?void 0:t.get)===null||r===void 0?void 0:r.call(t,"content-length"),i=n?parseInt(n,10):void 0;return this._bodySize=i,i},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"status",{get:function(){return this.response.status},enumerable:!1,configurable:!0}),e.prototype.text=function(){return x(this,void 0,void 0,function(){var t,r,n;return O(this,function(i){switch(i.label){case 0:this.clonedResponse||(this.clonedResponse=this.response.clone()),i.label=1;case 1:return i.trys.push([1,3,,4]),t=this.clonedResponse.text(),r=new Promise(function(o){return setTimeout(function(){return o(null)},TA)}),[4,Promise.race([t,r])];case 2:return n=i.sent(),[2,n];case 3:return i.sent(),[2,null];case 4:return[2]}})})},e.prototype.json=function(t,r){return t===void 0&&(t=[]),r===void 0&&(r=[]),x(this,void 0,void 0,function(){var n;return O(this,function(i){switch(i.label){case 0:return t.length===0?[2,null]:[4,this.text()];case 1:return n=i.sent(),[2,Fl(n,t,r)]}})})},e})(),kA=(function(){function e(t,r,n,i){this.statusCode=t,this.headersString=r,this.size=n,this.getJson=i}return Object.defineProperty(e.prototype,"bodySize",{get:function(){return this.size},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"status",{get:function(){return this.statusCode},enumerable:!1,configurable:!0}),e.prototype.headers=function(t){var r,n;if(t===void 0&&(t=[]),!this.headersString)return{};var i={},o=this.headersString.split(`\r -`);try{for(var s=ce(o),a=s.next();!a.done;a=s.next()){var u=a.value,c=oe(u.split(": "),2),l=c[0],f=c[1];l&&f&&(i[l]=f)}}catch(d){r={error:d}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}return Vs(i,{allow:t})},e.prototype.json=function(t,r){return t===void 0&&(t=[]),r===void 0&&(r=[]),x(this,void 0,void 0,function(){var n;return O(this,function(i){return t.length===0?[2,null]:(n=this.getJson(),n?(ug(n,t,r),[2,n]):[2,null])})})},e})();function Fl(e,t,r){if(!e)return null;try{var n=JSON.parse(e);return ug(n,t,r),n}catch{return null}}var Li;(function(e){e.REDACT="redact",e.REMOVE="remove"})(Li||(Li={}));var Zf="[REDACTED]",Vs=function(e,t){var r,n,i=t.allow,o=i===void 0?[]:i,s=t.strategy,a=s===void 0?Li.REMOVE:s,u=Ee([],oe(sT),!1),c={},l=function(v){var p=v.toLowerCase();u.find(function(b){return b.toLowerCase()===p})?a===Li.REDACT&&(c[v]=Zf):o.find(function(b){return b.toLowerCase()===p})?c[v]=e[v]:a===Li.REDACT&&(c[v]=Zf)};try{for(var f=ce(Object.keys(e)),d=f.next();!d.done;d=f.next()){var h=d.value;l(h)}}catch(v){r={error:v}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}return c},PA=(function(){function e(t,r,n,i,o,s,a,u,c,l,f){a===void 0&&(a=0),this.type=t,this.method=r,this.timestamp=n,this.startTime=i,this.url=o,this.requestWrapper=s,this.status=a,this.duration=u,this.responseWrapper=c,this.error=l,this.endTime=f}return e.prototype.toSerializable=function(){var t,r,n,i,o={type:this.type,method:this.method,url:this.url,timestamp:this.timestamp,status:this.status,duration:this.duration,error:this.error,startTime:this.startTime,endTime:this.endTime,requestHeaders:(t=this.requestWrapper)===null||t===void 0?void 0:t.headers(Ee([],oe(ms),!1)),requestBodySize:(r=this.requestWrapper)===null||r===void 0?void 0:r.bodySize,responseHeaders:(n=this.responseWrapper)===null||n===void 0?void 0:n.headers(Ee([],oe(ms),!1)),responseBodySize:(i=this.responseWrapper)===null||i===void 0?void 0:i.bodySize};return Object.fromEntries(Object.entries(o).filter(function(s){var a=oe(s,2);a[0];var u=a[1];return u!==void 0}))},e})();function RA(e){return typeof e=="object"&&e!==null&&"url"in e&&"method"in e}var OA=(function(){function e(t,r){r===void 0&&(r=Zt()),this.callback=t,this.id=r}return e})();function Kr(e){try{e()}catch{}}var xA=(function(){function e(t){this.eventCallbacks=new Map,this.isObserving=!1,this.logger=t;var r=le();e.isSupported()&&(this.globalScope=r)}return e.isSupported=function(){var t=le();return!!t&&!!t.fetch},e.prototype.subscribe=function(t,r){var n,i,o,s,a,u,c,l,f,d;if(this.logger||(this.logger=r),this.eventCallbacks.set(t.id,t),!this.isObserving){var h=(o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.XMLHttpRequest)===null||i===void 0?void 0:i.prototype)===null||o===void 0?void 0:o.open,v=(u=(a=(s=this.globalScope)===null||s===void 0?void 0:s.XMLHttpRequest)===null||a===void 0?void 0:a.prototype)===null||u===void 0?void 0:u.send,p=(f=(l=(c=this.globalScope)===null||c===void 0?void 0:c.XMLHttpRequest)===null||l===void 0?void 0:l.prototype)===null||f===void 0?void 0:f.setRequestHeader;h&&v&&p&&this.observeXhr(h,v,p);var b=(d=this.globalScope)===null||d===void 0?void 0:d.fetch;b&&this.observeFetch(b),this.isObserving=!0}},e.prototype.unsubscribe=function(t){this.eventCallbacks.delete(t.id)},e.prototype.triggerEventCallbacks=function(t){var r=this;this.eventCallbacks.forEach(function(n){try{n.callback(t)}catch(i){Kr(function(){var o;(o=r.logger)===null||o===void 0||o.debug("an unexpected error occurred while triggering event callbacks",i)})}})},e.prototype.handleNetworkRequestEvent=function(t,r,n,i,o,s,a){var u;if(!(s===void 0||a===void 0)){var c,l="GET";if(RA(r)?(c=r.url,l=r.method):c=(u=r?.toString)===null||u===void 0?void 0:u.call(r),c)try{var f=new URL(c);c="".concat(f.protocol,"//").concat(f.host).concat(f.pathname).concat(f.search).concat(f.hash)}catch{}l=n?.method||l;var d,h;i&&(d=i.status),o&&(h={name:o.name||"UnknownError",message:o.message||"An unknown error occurred"},d=0);var v=Math.floor(performance.now()-a),p=Math.floor(s+v),b=new PA(t,l,s,s,c,n,d,v,i,h,p);this.triggerEventCallbacks(b)}},e.prototype.getTimestamps=function(){var t,r;return{startTime:(t=Date.now)===null||t===void 0?void 0:t.call(Date),durationStart:(r=performance?.now)===null||r===void 0?void 0:r.call(performance)}},e.prototype.observeFetch=function(t){var r=this;!this.globalScope||!t||(this.globalScope.fetch=function(n,i){return x(r,void 0,void 0,function(){var o,s,a,u,c=this;return O(this,function(l){switch(l.label){case 0:try{o=this.getTimestamps()}catch(f){Kr(function(){var d;return(d=c.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while retrieving timestamps",f)})}l.label=1;case 1:return l.trys.push([1,3,,4]),[4,t(n,i)];case 2:return s=l.sent(),[3,4];case 3:return u=l.sent(),a=u,[3,4];case 4:try{this.handleNetworkRequestEvent("fetch",n,i?new AA(i):void 0,s?new IA(s):void 0,a,o?.startTime,o?.durationStart)}catch(f){Kr(function(){var d;return(d=c.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while handling fetch",f)})}if(s)return[2,s];throw a}})})})},e.createXhrJsonParser=function(t,r){return function(){var n;try{if(t.responseType==="json"){if(!((n=r.globalScope)===null||n===void 0)&&n.structuredClone)return r.globalScope.structuredClone(t.response)}else if(["text",""].includes(t.responseType))return JSON.parse(t.responseText)}catch(i){return i instanceof Error&&i.name==="InvalidStateError"&&Kr(function(){var o;return(o=r.logger)===null||o===void 0?void 0:o.debug("unexpected error when retrieving responseText. responseType='".concat(t.responseType,"'"))}),null}return null}},e.prototype.observeXhr=function(t,r,n){if(!(!this.globalScope||!t||!r)){var i=this.globalScope.XMLHttpRequest.prototype,o=this;i.open=function(){for(var s,a=[],u=0;u"u"||!document.title)return"";var t=document.querySelector("title");return t&&t.hasAttribute(In)?Mr:e?e(document.title):document.title};function hg(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var fi={},td;function DA(){if(td)return fi;td=1,Object.defineProperty(fi,"__esModule",{value:!0}),fi.Observable=void 0;const e=E=>!!Symbol[E],t=E=>e(E)?Symbol[E]:"@@"+E,r=t("iterator"),n=t("observable"),i=t("species");function o(E,m){let g=E[m];if(g!=null){if(typeof g!="function")throw new TypeError(g+" is not a function");return g}}function s(E){let m=E.constructor;return m!==void 0&&(m=m[i],m===null&&(m=void 0)),m!==void 0?m:b}function a(E){return E instanceof b}function u(E){u.log?u.log(E):setTimeout(()=>{throw E})}function c(E){Promise.resolve().then(()=>{try{E()}catch(m){u(m)}})}function l(E){let m=E._cleanup;if(m!==void 0&&(E._cleanup=void 0,!!m))try{if(typeof m=="function")m();else{let g=o(m,"unsubscribe");g&&g.call(m)}}catch(g){u(g)}}function f(E){E._observer=void 0,E._queue=void 0,E._state="closed"}function d(E){let m=E._queue;if(m){E._queue=void 0,E._state="ready";for(let g=0;gd(E));return}h(E,m,g)}}class p{constructor(m,g){this._cleanup=void 0,this._observer=m,this._queue=void 0,this._state="initializing";let y=this,_={get closed(){return y._state==="closed"},next(S){v(y,"next",S)},error(S){v(y,"error",S)},complete(){v(y,"complete")}};try{this._cleanup=g.call(void 0,_)}catch(S){_.error(S)}this._state==="initializing"&&(this._state="ready")}get closed(){return this._state==="closed"}unsubscribe(){this._state!=="closed"&&(f(this),l(this))}}class b{constructor(m){if(!(this instanceof b))throw new TypeError("Observable cannot be called as a function");if(typeof m!="function")throw new TypeError("Observable initializer must be a function");this._subscriber=m}subscribe(m){return(typeof m!="object"||m===null)&&(m={next:m,error:arguments[1],complete:arguments[2]}),new p(m,this._subscriber)}forEach(m){return new Promise((g,y)=>{if(typeof m!="function"){y(new TypeError(m+" is not a function"));return}function _(){S.unsubscribe(),g()}let S=this.subscribe({next(A){try{m(A,_)}catch(C){y(C),S.unsubscribe()}},error:y,complete:g})})}map(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>this.subscribe({next(_){try{_=m(_)}catch(S){return y.error(S)}y.next(_)},error(_){y.error(_)},complete(){y.complete()}}))}filter(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>this.subscribe({next(_){try{if(!m(_))return}catch(S){return y.error(S)}y.next(_)},error(_){y.error(_)},complete(){y.complete()}}))}reduce(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this),y=arguments.length>1,_=!1,A=arguments[1];return new g(C=>this.subscribe({next(I){let P=!_;if(_=!0,!P||y)try{A=m(A,I)}catch(R){return C.error(R)}else A=I},error(I){C.error(I)},complete(){if(!_&&!y)return C.error(new TypeError("Cannot reduce an empty sequence"));C.next(A),C.complete()}}))}async all(){let m=[];return await this.forEach(g=>m.push(g)),m}concat(...m){let g=s(this);return new g(y=>{let _,S=0;function A(C){_=C.subscribe({next(I){y.next(I)},error(I){y.error(I)},complete(){S===m.length?(_=void 0,y.complete()):A(g.from(m[S++]))}})}return A(this),()=>{_&&(_.unsubscribe(),_=void 0)}})}flatMap(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>{let _=[],S=this.subscribe({next(C){if(m)try{C=m(C)}catch(P){return y.error(P)}let I=g.from(C).subscribe({next(P){y.next(P)},error(P){y.error(P)},complete(){let P=_.indexOf(I);P>=0&&_.splice(P,1),A()}});_.push(I)},error(C){y.error(C)},complete(){A()}});function A(){S.closed&&_.length===0&&y.complete()}return()=>{_.forEach(C=>C.unsubscribe()),S.unsubscribe()}})}[n](){return this}static from(m){let g=typeof this=="function"?this:b;if(m==null)throw new TypeError(m+" is not an object");let y=o(m,n);if(y){let _=y.call(m);if(Object(_)!==_)throw new TypeError(_+" is not an object");return a(_)&&_.constructor===g?_:new g(S=>_.subscribe(S))}if(e("iterator")&&(y=o(m,r),y))return new g(_=>{c(()=>{if(!_.closed){for(let S of y.call(m))if(_.next(S),_.closed)return;_.complete()}})});if(Array.isArray(m))return new g(_=>{c(()=>{if(!_.closed){for(let S=0;S{c(()=>{if(!y.closed){for(let _=0;_0&&setTimeout(function(){c(new Error("".concat(t," timed out (id: ").concat(o,")"))),delete i.requestCallbacks[o]},n.timeout)});return a},e.prototype.handleResponse=function(t){var r;if(!this.requestCallbacks[t.id]){(r=this.logger)===null||r===void 0||r.warn("No callback found for request id: ".concat(t.id));return}this.requestCallbacks[t.id].resolve(t.responseData),delete this.requestCallbacks[t.id]},e.prototype.registerActionHandler=function(t,r){var n,i,o,s;this.actionHandlers.has(t)&&((s=(o=this.logger)===null||o===void 0?void 0:o.warn)===null||s===void 0||s.call(o,"Overwriting existing action handler for: ".concat(t))),this.actionHandlers.set(t,r);var a=this.pendingMessages.get(t);if(a){this.pendingMessages.delete(t);try{for(var u=ce(a),c=u.next();!c.done;c=u.next()){var l=c.value;r(l)}}catch(f){n={error:f}}finally{try{c&&!c.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}}},e.prototype.loadScriptOnce=function(t){return x(this,void 0,void 0,function(){var r,n,i;return O(this,function(o){switch(o.label){case 0:if(r=this.scriptLoadPromises.get(t),r)return[2,r];n=WA(t).then(function(){}),this.scriptLoadPromises.set(t,n),o.label=1;case 1:return o.trys.push([1,3,,4]),[4,n];case 2:return o.sent(),[3,4];case 3:throw i=o.sent(),this.scriptLoadPromises.delete(t),i;case 4:return[2]}})})},e.prototype.setup=function(t){var r=this,n,i,o=t===void 0?{}:t,s=o.logger,a=o.endpoint;s&&(this.logger=s),a&&this.endpoint===xu&&(this.endpoint=a),!this.isSetup&&(this.isSetup=!0,(i=(n=this.logger)===null||n===void 0?void 0:n.debug)===null||i===void 0||i.call(n,"Setting up messenger"),this.messageHandler=function(u){var c,l,f,d,h;if((l=(c=r.logger)===null||c===void 0?void 0:c.debug)===null||l===void 0||l.call(c,"Message received: ",JSON.stringify(u)),r.endpoint===u.origin){var v=u.data,p=v?.action;if(p)if("id"in v&&v.id)(d=(f=r.logger)===null||f===void 0?void 0:f.debug)===null||d===void 0||d.call(f,"Received Response to previous request: ",JSON.stringify(u)),r.handleResponse(v);else{if(p==="ping"){r.notify({action:"pong"});return}var b=r.actionHandlers.get(p);if(b)b(v.data);else{var E=(h=r.pendingMessages.get(p))!==null&&h!==void 0?h:[];E.push(v.data),r.pendingMessages.set(p,E)}}}},window.addEventListener("message",this.messageHandler),this.notify({action:"page-loaded"}))},e.prototype.destroy=function(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.isSetup=!1,this.actionHandlers.clear(),this.pendingMessages.clear(),this.requestCallbacks={},this.scriptLoadPromises.clear();var t=le();t?.[Es]===this&&delete t[Es]},e})();mg=Nu;function zA(e){return typeof e=="object"&&e!==null&&Nu in e&&e[Nu]===!0}function yg(e){var t=le(),r=t?.[Es];if(zA(r))return r;var n=new KA(e);return t&&(t[Es]=n),n}var id="__AMPLITUDE_BACKGROUND_CAPTURE__";function bg(e,t){var r,n=e;if(n[id]!==!0){n[id]=!0;var i=(r=void 0)!==null&&r!==void 0?r:$A,o=null,s=function(a,u){var c,l;a==="background-capture-complete"&&((l=(c=e.logger)===null||c===void 0?void 0:c.debug)===null||l===void 0||l.call(c,"Background capture complete"),e.notify({action:"background-capture-complete",data:u}))};e.registerActionHandler("initialize-background-capture",function(){var a,u;(u=(a=e.logger)===null||a===void 0?void 0:a.debug)===null||u===void 0||u.call(a,"Initializing background capture (external script)");var c=new URL(i,e.endpoint).toString();e.loadScriptOnce(c).then(function(){var l,f,d;(f=(l=e.logger)===null||l===void 0?void 0:l.debug)===null||f===void 0||f.call(l,"Background capture script loaded (external)"),o=(d=window?.amplitudeBackgroundCapture)===null||d===void 0?void 0:d.call(window,{messenger:e,onBackgroundCapture:s}),e.notify({action:"background-capture-loaded"})}).catch(function(){var l;(l=e.logger)===null||l===void 0||l.warn("Failed to initialize background capture")})}),e.registerActionHandler("close-background-capture",function(){var a;(a=o?.close)===null||a===void 0||a.call(o),o=null})}}var $o={always:"always",ifEmptyCampaign:"ifEmptyCampaign"},ti=function(e,t){return typeof e=="boolean"?e:e?.[t]!==!1},Eg=function(e){return ti(e,"attribution")},XA=function(e){return ti(e,"fileDownloads")},_g=function(e){return ti(e,"formInteractions")},Sg=function(e){return ti(e,"pageViews")},od=function(e){return ti(e,"sessions")},YA=function(e){return ti(e,"pageUrlEnrichment")},wg=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.networkTracking===!0||typeof e.networkTracking=="object")},Tg=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.elementInteractions===!0||typeof e.elementInteractions=="object")},JA=function(e){return typeof e=="boolean"?e:typeof e=="object"&&e.webVitals===!0},Ag=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.frustrationInteractions===!0||typeof e.frustrationInteractions=="object")},QA=function(e){return typeof e=="boolean"?e:typeof e=="object"&&e!==null&&e.enabled!==!1},ZA=function(e){if(Tg(e.autocapture)&&typeof e.autocapture=="object"&&typeof e.autocapture.elementInteractions=="object")return e.autocapture.elementInteractions},eC=function(e){if(Ag(e.autocapture)&&typeof e.autocapture=="object"&&typeof e.autocapture.frustrationInteractions=="object")return e.autocapture.frustrationInteractions},tC=function(e){var t;if(wg(e.autocapture)){var r=void 0;return typeof e.autocapture=="object"&&typeof e.autocapture.networkTracking=="object"?r=e.autocapture.networkTracking:e.networkTrackingOptions&&(r=e.networkTrackingOptions),D(D({},r),{captureRules:(t=r?.captureRules)===null||t===void 0?void 0:t.map(function(n){var i,o,s;if(!((i=n.urls)===null||i===void 0)&&i.length&&(!((o=n.hosts)===null||o===void 0)&&o.length)){var a=JSON.stringify(n.hosts),u=JSON.stringify(n.urls);return(s=e.loggerProvider)===null||s===void 0||s.warn("Found network capture rule with both urls='".concat(u,"' and hosts='").concat(a,"' set. ")+"Definition of urls takes precedence over hosts, so ignoring hosts."),D(D({},n),{hosts:void 0})}return n})})}},sd=function(e){var t=function(){return!1},r=void 0,n,i=e.pageCounter,o=Sg(e.defaultTracking);return o&&(t=void 0,n=void 0,e.defaultTracking&&typeof e.defaultTracking=="object"&&e.defaultTracking.pageViews&&typeof e.defaultTracking.pageViews=="object"&&("trackOn"in e.defaultTracking.pageViews&&(t=e.defaultTracking.pageViews.trackOn),"trackHistoryChanges"in e.defaultTracking.pageViews&&(r=e.defaultTracking.pageViews.trackHistoryChanges),"eventType"in e.defaultTracking.pageViews&&e.defaultTracking.pageViews.eventType&&(n=e.defaultTracking.pageViews.eventType))),{trackOn:t,trackHistoryChanges:r,eventType:n,pageCounter:i}},ad=function(e){return Eg(e.defaultTracking)&&e.defaultTracking&&typeof e.defaultTracking=="object"&&e.defaultTracking.attribution&&typeof e.defaultTracking.attribution=="object"?D({},e.defaultTracking.attribution):{}},rC=function(e){if(_g(e.defaultTracking)&&e.defaultTracking&&typeof e.defaultTracking=="object"&&typeof e.defaultTracking.formInteractions=="object")return e.defaultTracking.formInteractions},Ca=function(e,t){for(var r=0;r"u"&&u(new Error("XHRTransport is not supported."));var c=new XMLHttpRequest;c.open("POST",r,!0),c.onreadystatechange=function(){if(c.readyState===o.state.done){var p=c.responseText;try{a(o.buildResponse(JSON.parse(p)))}catch{a(o.buildResponse({code:c.status}))}}};var l={"Content-Type":"application/json",Accept:"*/*"},f=JSON.stringify(n),d=i&&f.length>=Zp&&eg(),h=function(p){var b,E;l=D(D({},o.customHeaders),l);try{for(var m=ce(Object.entries(l)),g=m.next();!g.done;g=m.next()){var y=oe(g.value,2),_=y[0],S=y[1];c.setRequestHeader(_,S)}}catch(A){b={error:A}}finally{try{g&&!g.done&&(E=m.return)&&E.call(m)}finally{if(b)throw b.error}}c.send(p)},v=function(){return x(o,void 0,void 0,function(){var p;return O(this,function(b){switch(b.label){case 0:return d?[4,tg(f)]:[3,2];case 1:return p=b.sent(),p?(l["Content-Encoding"]="gzip",h(p)):h(f),[3,3];case 2:h(f),b.label=3;case 3:return[2]}})})};v().catch(u)})]})})},t})(no),uC=(function(e){Dt(t,e);function t(r){r===void 0&&(r={});var n=e.call(this)||this;return n.customHeaders=r,n}return t.prototype.send=function(r,n,i){return i===void 0&&(i=!1),x(this,void 0,void 0,function(){var o,s,a,u,c,l,f,d;return O(this,function(h){switch(h.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return o=JSON.stringify(n),s=i&&o.length>=Zp&&eg(),a=o,u={"Content-Type":"application/json",Accept:"*/*"},s?[4,tg(o)]:[3,2];case 1:c=h.sent(),c&&(u["Content-Encoding"]="gzip",a=c),h.label=2;case 2:return u=D(D({},this.customHeaders),u),l={headers:u,body:a,method:"POST"},[4,fetch(r,l)];case 3:return f=h.sent(),[4,f.text()];case 4:d=h.sent();try{return[2,this.buildResponse(JSON.parse(d))]}catch{return[2,this.buildResponse({code:f.status})]}return[2]}})})},t})(no),lC=(function(e){Dt(t,e);function t(){return e.call(this)||this}return t.prototype.send=function(r,n,i){return x(this,void 0,void 0,function(){var o=this;return O(this,function(s){return[2,new Promise(function(a,u){var c=le();if(!c?.navigator.sendBeacon)throw new Error("SendBeaconTransport is not supported");try{var l=JSON.stringify(n),f=c.navigator.sendBeacon(r,l);return a(f?o.buildResponse({code:200,events_ingested:n.events.length,payload_size_bytes:l.length,server_upload_time:Date.now()}):o.buildResponse({code:500}))}catch(d){u(d)}})]})})},t})(no),cC=function(e,t,r){return r===void 0&&(r=!0),x(void 0,void 0,void 0,function(){var n,i,o,s,a,u,c,l,f;return O(this,function(d){switch(d.label){case 0:return n=jT(e),[4,t.getRaw(n)];case 1:return i=d.sent(),i?r?[4,t.remove(n)]:[3,3]:[2,{optOut:!1}];case 2:d.sent(),d.label=3;case 3:return o=oe(i.split("."),6),s=o[0],a=o[1],u=o[2],c=o[3],l=o[4],f=o[5],[2,{deviceId:s,userId:fC(a),sessionId:ka(c),lastEventId:ka(f),lastEventTime:ka(l),optOut:!!u}]}})})},ka=function(e){var t=parseInt(e,32);if(!isNaN(t))return t},fC=function(e){if(!(!atob||!escape||!e))try{return decodeURIComponent(escape(atob(e)))}catch{return}},$t="[Amplitude]",ud="".concat($t," Form Started"),dC="".concat($t," Form Submitted"),hC="".concat($t," File Downloaded"),ld="session_start",cd="session_end",vC="".concat($t," File Extension"),pC="".concat($t," File Name"),gC="".concat($t," Link ID"),mC="".concat($t," Link Text"),yC="".concat($t," Link URL"),Pa="".concat($t," Form ID"),Ra="".concat($t," Form Name"),Oa="".concat($t," Form Destination"),_s="cookie",kg="US";const bC=Object.freeze(Object.defineProperty({__proto__:null,DEFAULT_ACTION_CLICK_ALLOWLIST:og,DEFAULT_CSS_SELECTOR_ALLOWLIST:ig,DEFAULT_DATA_ATTRIBUTE_PREFIX:Ul,EXCLUDE_INTERNAL_REFERRERS_CONDITIONS:$o,get IdentifyOperation(){return vr},get LogLevel(){return at},OfflineDisabled:ng,get RevenueProperty(){return Ft},get ServerZone(){return Jn},get SpecialEventType(){return St}},Symbol.toStringTag,{value:"Module"}));var fd=function(e){var t=e.split(".");return t.length<=2?e:t.slice(t.length-2,t.length).join(".")},EC=function(e){return Object.values(e).every(function(t){return!t})},_C=function(e){var t=D(D({},e),{referring_domain:void 0,referrer:void 0});return Object.values(t).every(function(r){return!r})},SC=function(e,t,r,n,i,o){i===void 0&&(i=!0),e.referrer;var s=e.referring_domain,a=gs(e,["referrer","referring_domain"]),u=t||{};u.referrer;var c=u.referring_domain,l=gs(u,["referrer","referring_domain"]),f=r.excludeInternalReferrers;if(f){var d=IC(f,n);if(!(d instanceof TypeError)&&e.referring_domain&&kC(e.referring_domain,o)){if(d==="always")return dd(d,e.referring_domain,n),!1;if(d==="ifEmptyCampaign"&&_C(e))return dd(d,e.referring_domain,n),!1}}if(wC(r.excludeReferrers,e.referring_domain))return n.debug("This is not a new campaign because ".concat(e.referring_domain," is in the exclude referrer list.")),!1;if(!i&&EC(e)&&t)return n.debug("This is not a new campaign because this is a direct traffic in the same session."),!1;var h=JSON.stringify(a)!==JSON.stringify(l),v=fd(s||"")!==fd(c||""),p=!t||h||v;return p?n.debug("This is a new campaign. An $identify event will be sent."):n.debug("This is not a new campaign because it's the same as the previous one."),p},wC=function(e,t){return e===void 0&&(e=[]),t===void 0&&(t=""),e.some(function(r){return r instanceof RegExp?r.test(t):r===t})},TC=function(e,t){var r=t.startsWith(".")?t:".".concat(t),n=e.startsWith(".")?e:".".concat(e);return!!n.endsWith(r)},AC=function(e,t){var r=D(D({},js),e),n=Object.entries(r).reduce(function(i,o){var s,a=oe(o,2),u=a[0],c=a[1];return i.setOnce("initial_".concat(u),(s=c??t.initialEmptyValue)!==null&&s!==void 0?s:"EMPTY"),c?i.set(u,c):i.unset(u)},new Fn);return Ll(n)},CC=function(e){var t=e;return t?(t.startsWith(".")&&(t=t.substring(1)),[new RegExp("".concat(t.replace(".","\\."),"$"))]):[]},IC=function(e,t){if(e===!0)return $o.always;if(typeof e=="object"){var r=e.condition;if(typeof r=="string"&&Object.keys($o).includes(r))return r;if(typeof r>"u")return $o.always}var n="Invalid configuration provided for attribution.excludeInternalReferrers: ".concat(JSON.stringify(e));return t.error(n),new TypeError(n)};function dd(e,t,r){var n="This is not a new campaign because referring_domain=".concat(t," is on the same domain as the current page and it is configured to exclude internal referrers");e==="always"?r.debug(n):e==="ifEmptyCampaign"&&r.debug("".concat(n," with empty campaign parameters"))}var Pg=["ac.in","ac.jp","ac.kr","ac.th","ac.uk","ac.za","appspot.com","asn.au","azurewebsites.net","cloudfront.net","myshopify.com","blogspot.com","co.ca","co.in","co.jp","co.kr","co.nz","co.th","co.uk","co.za","com.ar","com.au","com.br","com.cn","com.hk","com.in","com.jp","com.kr","com.mx","com.pl","com.sg","com.tr","com.tw","ed.jp","edu.au","edu.br","edu.cn","edu.hk","edu.sg","edu.th","edu.tr","edu.tw","firebaseapp.com","fly.dev","gc.ca","geek.nz","github.io","gitlab.io","go.jp","go.kr","go.th","gob.ar","gob.mx","gov.au","gov.br","gov.cn","gov.hk","gov.in","gov.pl","gov.sg","gov.tr","gov.tw","gov.uk","gov.za","govt.nz","gr.jp","herokuapp.com","id.au","idv.hk","iwi.nz","lg.jp","ltd.uk","maori.nz","me.uk","mil.kr","ne.jp","ne.kr","net.au","net.br","net.cn","net.hk","net.in","net.nz","net.pl","net.sg","net.tr","net.tw","net.za","onrender.com","or.jp","or.kr","or.th","org.ar","org.au","org.br","org.cn","org.hk","org.in","org.mx","org.nz","org.pl","org.sg","org.tw","org.uk","org.za","pages.dev","pe.kr","plc.uk","re.kr","res.in","sch.uk","vercel.app","netlify.app","workers.dev"],Rg=function(e){var t,r,n=e||((r=(t=le())===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.hostname);if(!n)return"";var i=n.split("."),o=i[i.length-1],s=i[i.length-2];return Pg.find(function(a){return n.endsWith(".".concat(a))})&&(o=i[i.length-2]+"."+i[i.length-1],s=i[i.length-3]),s?"".concat(s,".").concat(o):o},kC=function(e,t){var r=le();if(!r)return!1;var n=(t||"").trim()||Rg(r.location.hostname);return TC(e,n)},PC=(function(e){Dt(t,e);function t(r,n,i,o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A,C,I,P,R,k,N,$,ee,B,z,K,Q,ae,fe,pe,ye,be,de,me,M,Z,X,re){i===void 0&&(i=new Dl),o===void 0&&(o={domain:"",expiration:365,sameSite:"Lax",secure:!1,upgrade:!0}),c===void 0&&(c=1e3),l===void 0&&(l=5),f===void 0&&(f=30),d===void 0&&(d=_s),E===void 0&&(E=new Xn),m===void 0&&(m=at.Warn),y===void 0&&(y=!1),_===void 0&&(_=!1),C===void 0&&(C=""),I===void 0&&(I=kg),k===void 0&&(k=1800*1e3),N===void 0&&(N=new Ig({loggerProvider:E})),$===void 0&&($={ipAddress:!0,language:!0,platform:!0}),ee===void 0&&(ee="fetch"),B===void 0&&(B=!1),z===void 0&&(z=!0),ye===void 0&&(ye=!0),be===void 0&&(be=0),Z===void 0&&(Z=!1),X===void 0&&(X=!1);var F=this,ge;F=e.call(this,{apiKey:r,storageProvider:N,transportProvider:Og(ee)})||this,F.apiKey=r,F.appVersion=n,F.cookieOptions=o,F.defaultTracking=s,F.autocapture=a,F.flushIntervalMillis=c,F.flushMaxRetries=l,F.flushQueueSize=f,F.identityStorage=d,F.ingestionMetadata=h,F.instanceName=v,F.loggerProvider=E,F.logLevel=m,F.minIdLength=g,F.offline=y,F.partnerId=S,F.plan=A,F.serverUrl=C,F.serverZone=I,F.sessionTimeout=k,F.storageProvider=N,F.trackingOptions=$,F.transport=ee,F.useBatch=B,F.fetchRemoteConfig=z,F.networkTrackingOptions=fe,F.identify=pe,F.enableDiagnostics=ye,F.diagnosticsSampleRate=be,F.diagnosticsClient=de,F.remoteConfig=me,F.topLevelDomain=M,F.enableRequestBodyCompression=Z,F._enableRequestBodyCompressionExperimental=X,F.customEnrichment=re,F.version=ql,F._optOut=!1,F._cookieStorage=i,F.deviceId=u,F.lastEventId=p,F.lastEventTime=b,F.optOut=_,F.deferredSessionId=R,F.sessionId=P,F.pageCounter=Q,F.userId=K,F.debugLogsEnabled=ae,F.loggerProvider.enable(ae?at.Debug:F.logLevel),F.networkTrackingOptions=fe,F.identify=pe,F.enableDiagnostics=ye,F.diagnosticsSampleRate=be,F.diagnosticsClient=de;var w=(ge=me?.fetchRemoteConfig)!==null&&ge!==void 0?ge:z;return F.remoteConfig=F.remoteConfig||{},F.remoteConfig.fetchRemoteConfig=w,F.fetchRemoteConfig=w,F.topLevelDomain=M||Rg(),F}return Object.defineProperty(t.prototype,"cookieStorage",{get:function(){return this._cookieStorage},set:function(r){this._cookieStorage!==r&&(this._cookieStorage=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"deviceId",{get:function(){return this._deviceId},set:function(r){this._deviceId!==r&&(this._deviceId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"userId",{get:function(){return this._userId},set:function(r){this._userId!==r&&(this._userId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"sessionId",{get:function(){return this._sessionId},set:function(r){this._sessionId!==r&&(this._sessionId=r,r!==void 0&&this._deferredSessionId!==void 0&&(this._deferredSessionId=void 0),this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"deferredSessionId",{get:function(){return this._deferredSessionId},set:function(r){this._deferredSessionId!==r&&r!==this.sessionId&&(this._deferredSessionId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optOut",{get:function(){return this._optOut},set:function(r){this._optOut!==r&&(this._optOut=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"lastEventTime",{get:function(){return this._lastEventTime},set:function(r){this._lastEventTime!==r&&(this._lastEventTime=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"lastEventId",{get:function(){return this._lastEventId},set:function(r){this._lastEventId!==r&&(this._lastEventId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"pageCounter",{get:function(){return this._pageCounter},set:function(r){this._pageCounter!==r&&(this._pageCounter=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"debugLogsEnabled",{set:function(r){this._debugLogsEnabled!==r&&(this._debugLogsEnabled=r,this.updateStorage())},enumerable:!1,configurable:!0}),t.prototype.updateStorage=function(){var r={deviceId:this._deviceId,userId:this._userId,sessionId:this._sessionId,deferredSessionId:this._deferredSessionId,optOut:this._optOut,lastEventTime:this._lastEventTime,lastEventId:this._lastEventId,pageCounter:this._pageCounter,debugLogsEnabled:this._debugLogsEnabled,cookieDomain:void 0};this.cookieStorage instanceof bs&&(r.cookieDomain=this.cookieStorage.options.domain),this.cookieStorage.set(Yp(this.apiKey),r)},t})(Kp),RC=function(e,t,r,n,i){return t===void 0&&(t={}),x(void 0,void 0,void 0,function(){var o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A,C,I,P,R,k,N,$,ee,B,z,K,Q,ae,fe,pe,ye,be,de,me,M,Z,X,re,F,ge,w,T,L,H,U,q,te;return O(this,function(G){switch(G.label){case 0:return o=t.identityStorage||_s,s="",o===_s&&!(!((P=t.cookieOptions)===null||P===void 0)&&P.domain)&&((R=t.cookieOptions)===null||R===void 0?void 0:R.domain)!==""?[4,NC(void 0,n)]:[3,2];case 1:s=G.sent(),G.label=2;case 2:return a=D({domain:(N=(k=t.cookieOptions)===null||k===void 0?void 0:k.domain)!==null&&N!==void 0?N:s,expiration:365,sameSite:"Lax",secure:!1,upgrade:!0},t.cookieOptions),u={duplicateResolverFn:function(Y){var V=Jp(Y);if(!V)return!1;var W=JSON.parse(V);return Qp(W.cookieDomain,a.domain)},diagnosticsClient:n},c=OC(t.identityStorage,a,u),[4,cC(e,c,(ee=($=t.cookieOptions)===null||$===void 0?void 0:$.upgrade)!==null&&ee!==void 0?ee:!0)];case 3:return l=G.sent(),[4,c.get(Yp(e))];case 4:return f=G.sent(),d=ys(),h=d.ampTimestamp?Number(d.ampTimestamp):void 0,v=h?Date.now()"u"||!location.hostname)return[2,""];if(r=location.hostname,n=r.split("."),n.length===1)return[2,""];for(i=[],o=1,Pg.find(function(f){return r.endsWith(".".concat(f))})&&(o=2),s=n.length-o-1;s>=0;--s)i.push(n.slice(s).join("."));s=0,l.label=2;case 2:if(!(s"u"||typeof a=="function"&&a()},v=typeof location<"u"?location.href:null,p=function(){return x(void 0,void 0,void 0,function(){var m,g,y,_,S;return O(this,function(A){switch(A.label){case 0:return m=location.href,g=FC(u,m,v||"")&&h(),v=m,g?(y=void 0,s&&(y=Zt(),s.set(xa,{pageViewId:y})),n?.log("Tracking page view event"),t!=null?[3,1]:[3,3]):[3,4];case 1:return S=(_=t).track,[4,d(y)];case 2:S.apply(_,[A.sent()]),A.label=3;case 3:A.label=4;case 4:return[2]}})})},b=function(){p()},E={name:"@amplitude/plugin-page-view-tracking-browser",type:"enrichment",setup:function(m,g){return x(void 0,void 0,void 0,function(){var y,_,S;return O(this,function(A){switch(A.label){case 0:if(t=g,o=m,n=m.loggerProvider,n.log("Installing @amplitude/plugin-page-view-tracking-browser"),i=!0,r){try{s=new qs(r.sessionStorage)}catch{n?.debug("sessionStorage is not available in this environment.")}r.addEventListener("popstate",b),r.history.pushState=new Proxy(r.history.pushState,{apply:function(C,I,P){var R=oe(P,3),k=R[0],N=R[1],$=R[2];C.apply(I,[k,N,$]),i&&b()}})}return h()?(n.log("Tracking page view event"),y=void 0,s&&(y=Zt(),s.set(xa,{pageViewId:y})),S=(_=t).track,[4,d(y)]):[3,2];case 1:S.apply(_,[A.sent()]),A.label=2;case 2:return[2]}})})},execute:function(m){return x(void 0,void 0,void 0,function(){var g,y,_;return O(this,function(S){switch(S.label){case 0:return a==="attribution"&&UC(m)?(n?.log("Enriching campaign event to page view event with campaign parameters"),g=void 0,s?[4,s.get(xa)]:[3,2]):[3,4];case 1:y=S.sent(),g=y?.pageViewId,S.label=2;case 2:return[4,d(g)];case 3:_=S.sent(),m.event_type=_.event_type,m.event_properties=D(D({},m.event_properties),_.event_properties),S.label=4;case 4:return o&&m.event_type===l&&(o.pageCounter=o.pageCounter?o.pageCounter+1:1,m.event_properties=D(D({},m.event_properties),{"[Amplitude] Page Counter":o.pageCounter})),[2,m]}})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(m){return r&&(r.removeEventListener("popstate",b),i=!1),[2]})})}};return E},DC=function(){return x(void 0,void 0,void 0,function(){var e;return O(this,function(t){switch(t.label){case 0:return e=LC,[4,new dg().parse()];case 1:return[2,e.apply(void 0,[t.sent()])]}})})},UC=function(e){if(e.event_type==="$identify"&&e.user_properties){var t=e.user_properties,r=t[vr.SET]||{},n=t[vr.UNSET]||{},i=Ee(Ee([],oe(Object.keys(r)),!1),oe(Object.keys(n)),!1);return Object.keys(js).every(function(o){return i.includes(o)})}return!1},FC=function(e,t,r){switch(e){case"pathOnly":{if(r=="")return!0;var n=new URL(t),i=new URL(r),o=n.origin+n.pathname,s=i.origin+i.pathname;return o!==s}default:return t!==r}},HC=function(){var e,t=[],r=function(l,f,d){l.addEventListener(f,d),t.push({element:l,type:f,handler:d})},n=function(){t.forEach(function(l){var f=l.element,d=l.type,h=l.handler;f?.removeEventListener(d,h)}),t=[]},i,o="@amplitude/plugin-form-interaction-tracking-browser",s="enrichment",a=function(l,f){return x(void 0,void 0,void 0,function(){var d,h;return O(this,function(v){return i=rC(l),d=function(){if(!f){l.loggerProvider.warn("Form interaction tracking requires a later version of @amplitude/analytics-browser. Form interaction events are not tracked.");return}if(!(typeof document>"u")){var p=new WeakSet,b=function(m){if(!p.has(m)){p.add(m);var g=!1;r(m,"change",function(){var y,_=vd(m);g||f.track(ud,(y={},y[Pa]=yn(m.id),y[Ra]=yn(m.name),y[Oa]=_,y)),g=!0}),r(m,"submit",function(y){var _,S,A=vd(m);if(g||f.track(ud,(_={},_[Pa]=yn(m.id),_[Ra]=yn(m.name),_[Oa]=A,_)),g=!0,i?.shouldTrackSubmit!==void 0)if(typeof i.shouldTrackSubmit=="function"&&typeof SubmitEvent<"u"&&y instanceof SubmitEvent)try{var C=i.shouldTrackSubmit(y);if(!C)return}catch{l.loggerProvider.warn("shouldTrackSubmit callback threw an error, proceeding with tracking.")}else l.loggerProvider.warn("shouldTrackSubmit is ignored because it is not a function or event is not a SubmitEvent.");f.track(dC,(S={},S[Pa]=yn(m.id),S[Ra]=yn(m.name),S[Oa]=A,S)),g=!1})}},E=Array.from(document.getElementsByTagName("form"));E.forEach(b),typeof MutationObserver<"u"&&(e=new MutationObserver(function(m){m.forEach(function(g){g.addedNodes.forEach(function(y){y.nodeName==="FORM"&&b(y),"querySelectorAll"in y&&typeof y.querySelectorAll=="function"&&Array.from(y.querySelectorAll("form")).map(b)})})}),e.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?d():(h=le(),h?h.addEventListener("load",d):l.loggerProvider.debug("Form interaction tracking is not installed because global is undefined.")),[2]})})},u=function(l){return x(void 0,void 0,void 0,function(){return O(this,function(f){return[2,l]})})},c=function(){return x(void 0,void 0,void 0,function(){return O(this,function(l){return e?.disconnect(),n(),[2]})})};return{name:o,type:s,setup:a,execute:u,teardown:c}},yn=function(e){if(typeof e=="string")return e},vd=function(e){var t=e.getAttribute("action");try{t=new URL(encodeURI(t??""),window.location.href).href}catch{}return t},BC=function(){var e,t=[],r=function(c,l,f){c.addEventListener(l,f),t.push({element:c,type:l,handler:f})},n=function(){t.forEach(function(c){var l=c.element,f=c.type,d=c.handler;l?.removeEventListener(f,d)}),t=[]},i="@amplitude/plugin-file-download-tracking-browser",o="enrichment",s=function(c,l){return x(void 0,void 0,void 0,function(){var f,d;return O(this,function(h){return f=function(){if(!l){c.loggerProvider.warn("File download tracking requires a later version of @amplitude/analytics-browser. File download events are not tracked.");return}if(!(typeof document>"u")){var v=function(E){var m;try{m=new URL(E.href,window.location.href)}catch{return}var g=p.exec(m.href),y=g?.[1];y&&r(E,"click",function(){var _;y&&l.track(hC,(_={},_[vC]=y,_[pC]=m.pathname,_[gC]=E.id,_[mC]=E.text,_[yC]=E.href,_))})},p=/\.(pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma)(\?.+)?$/,b=Array.from(document.getElementsByTagName("a"));b.forEach(v),typeof MutationObserver<"u"&&(e=new MutationObserver(function(E){E.forEach(function(m){m.addedNodes.forEach(function(g){g.nodeName==="A"&&v(g),"querySelectorAll"in g&&typeof g.querySelectorAll=="function"&&Array.from(g.querySelectorAll("a")).map(v)})})}),e.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?f():(d=le(),d?d.addEventListener("load",f):c.loggerProvider.debug("File download tracking is not installed because global is undefined.")),[2]})})},a=function(c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return[2,c]})})},u=function(){return x(void 0,void 0,void 0,function(){return O(this,function(c){return e?.disconnect(),n(),[2]})})};return{name:i,type:o,setup:s,execute:a,teardown:u}},pd=!1,jC=function(e){if(!(pd||e.defaultTracking!==void 0)){var t=`\`options.defaultTracking\` is set to undefined. This implicitly configures your Amplitude instance to track Page Views, Sessions, File Downloads, and Form Interactions. You can suppress this warning by explicitly setting a value to \`options.defaultTracking\`. The value must either be a boolean, to enable and disable all default events, or an object, for advanced configuration. For example: - -amplitude.init(, { - defaultTracking: true, -}); - -Visit https://www.docs.developers.amplitude.com/data/sdks/browser-2/#tracking-default-events for more details.`;e.loggerProvider.warn(t),pd=!0}},qC=function(){var e="@amplitude/plugin-network-checker-browser",t="before",r=le(),n=[],i=function(u,c){r?.addEventListener&&(r?.addEventListener(u,c),n.push({type:u,handler:c}))},o=function(){n.forEach(function(u){var c=u.type,l=u.handler;r?.removeEventListener(c,l)}),n=[]},s=function(u,c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return typeof navigator>"u"?(u.loggerProvider.debug("Network connectivity checker plugin is disabled because navigator is not available."),u.offline=!1,[2]):(u.offline=!navigator.onLine,i("online",function(){u.loggerProvider.debug("Network connectivity changed to online."),u.offline=!1,setTimeout(function(){c.flush()},u.flushIntervalMillis)}),i("offline",function(){u.loggerProvider.debug("Network connectivity changed to offline."),u.offline=!0}),[2])})})},a=function(){return x(void 0,void 0,void 0,function(){return O(this,function(u){return o(),[2]})})};return{name:e,type:t,setup:s,teardown:a}};function xg(e){var t,r,n,i,o,s,a,u,c,l,f,d;if(!(typeof e!="object"||e===null)&&!Array.isArray(e)){var h=Object.keys(e);try{for(var v=ce(h),p=v.next();!p.done;p=v.next()){var b=p.value;try{var E=e[b];typeof E?.enabled=="boolean"&&(E.enabled?(delete E.enabled,Object.keys(E).length===0&&(e[b]=!0)):e[b]=!1),xg(E)}catch{}}}catch($){t={error:$}}finally{try{p&&!p.done&&(r=v.return)&&r.call(v)}finally{if(t)throw t.error}}try{if(!((c=(u=(a=e.autocapture)===null||a===void 0?void 0:a.networkTracking)===null||u===void 0?void 0:u.captureRules)===null||c===void 0)&&c.length)try{for(var m=ce(e.autocapture.networkTracking.captureRules),g=m.next();!g.done;g=m.next()){var y=g.value;try{for(var _=(o=void 0,ce(["responseHeaders","requestHeaders"])),S=_.next();!S.done;S=_.next()){var A=S.value,C=(l=y[A])!==null&&l!==void 0?l:{},I=C.captureSafeHeaders,P=C.allowlist;if(!(!I&&!P)){if(P!==void 0&&!Array.isArray(P)){delete y[A];continue}y[A]=Ee(Ee([],oe(I?ms:[]),!1),oe(P??[]),!1)}}}catch($){o={error:$}}finally{try{S&&!S.done&&(s=_.return)&&s.call(_)}finally{if(o)throw o.error}}}}catch($){n={error:$}}finally{try{g&&!g.done&&(i=m.return)&&i.call(m)}finally{if(n)throw n.error}}}catch{}var R=(f=e.autocapture)===null||f===void 0?void 0:f.frustrationInteractions;R&&(R.rageClick&&(R.rageClicks=R.rageClick,delete R.rageClick),R.deadClick&&(R.deadClicks=R.deadClick,delete R.deadClick));try{var k=(d=e.autocapture)===null||d===void 0?void 0:d.elementInteractions;if(k&&typeof k=="object"&&(k.viewportContentUpdated===!0&&(k.viewportContentUpdated={}),k.viewportContentUpdated===!1&&(k.viewportContentUpdated={enabled:!1}),k.exposureDuration!==void 0)){var N=k.viewportContentUpdated;N===void 0?k.viewportContentUpdated={exposureDuration:k.exposureDuration}:typeof N=="object"&&N.exposureDuration===void 0&&N.enabled!==!1&&(N.exposureDuration=k.exposureDuration),delete k.exposureDuration}}catch{}}}function gd(e,t,r){var n,i,o=[];try{for(var s=ce(t??[]),a=s.next();!a.done;a=s.next()){var u=a.value;try{o.push(new RegExp(u))}catch(c){r.loggerProvider.warn("Invalid regex pattern: ".concat(u),c)}}}catch(c){n={error:c}}finally{try{a&&!a.done&&(i=s.return)&&i.call(s)}finally{if(n)throw n.error}}return e.concat(o)}function VC(e,t){var r,n,i,o,s,a,u;if(e){xg(e);try{t.loggerProvider.debug("Update browser config with remote configuration:",JSON.stringify(e));var c=e;if(c&&"autocapture"in c){if(typeof c.autocapture=="boolean"&&(t.autocapture=c.autocapture),typeof c.autocapture=="object"&&c.autocapture!==null){var l=D({},c.autocapture);if(t.autocapture===void 0&&(t.autocapture=c.autocapture),typeof c.autocapture.elementInteractions=="object"&&c.autocapture.elementInteractions!==null&&(!((i=c.autocapture.elementInteractions.pageUrlAllowlistRegex)===null||i===void 0)&&i.length)){l.elementInteractions=D({},c.autocapture.elementInteractions);var f=l.elementInteractions,d=(o=f.pageUrlAllowlist)!==null&&o!==void 0?o:[],h=c.autocapture.elementInteractions.pageUrlAllowlistRegex;f.pageUrlAllowlist=gd(d,h,t),delete f.pageUrlAllowlistRegex}if(typeof c.autocapture.networkTracking=="object"&&c.autocapture.networkTracking!==null&&(!((s=c.autocapture.networkTracking.captureRules)===null||s===void 0)&&s.length)){l.networkTracking=D({},c.autocapture.networkTracking);var v=l.networkTracking,p=(a=v.captureRules)!==null&&a!==void 0?a:[];try{for(var b=ce(p),E=b.next();!E.done;E=b.next()){var m=E.value;m.urls=gd((u=m.urls)!==null&&u!==void 0?u:[],m.urlsRegex,t),delete m.urlsRegex}}catch(g){r={error:g}}finally{try{E&&!E.done&&(n=b.return)&&n.call(b)}finally{if(r)throw r.error}}}typeof t.autocapture=="boolean"&&(t.autocapture=D({attribution:t.autocapture,fileDownloads:t.autocapture,formInteractions:t.autocapture,pageViews:t.autocapture,sessions:t.autocapture,elementInteractions:t.autocapture,webVitals:t.autocapture,frustrationInteractions:t.autocapture},l)),typeof t.autocapture=="object"&&(t.autocapture=D(D({},t.autocapture),l))}t.defaultTracking=t.autocapture}"customEnrichment"in c&&c.customEnrichment!==null&&(t.customEnrichment=c.customEnrichment),t.loggerProvider.debug("Browser config after remote config update:",JSON.stringify(t))}catch(g){t.loggerProvider.error("Failed to apply remote configuration because of error: ",g)}}}var Ng="1.25.1",$C="@amplitude/plugin-autocapture-browser",WC="@amplitude/plugin-frustration-browser",Lg="[Amplitude] Element Clicked",GC="[Amplitude] Dead Click",KC="[Amplitude] Rage Click",zC="[Amplitude] Error Click",XC="[Amplitude] Element Changed",YC="[Amplitude] Thrashed Cursor",JC="[Amplitude] Element ID",QC="[Amplitude] Element Class",md="[Amplitude] Element Tag",yd="[Amplitude] Element Text",ZC="[Amplitude] Element Hierarchy",eI="[Amplitude] Element Href",tI="[Amplitude] Element Position Left",rI="[Amplitude] Element Position Top",nI="[Amplitude] Element Aria Label",iI="[Amplitude] Element Attributes",oI="[Amplitude] Element Path",sI="[Amplitude] Element Parent Label",Lu="[Amplitude] Page URL",aI="[Amplitude] Page Title",Mg="[Amplitude] Viewport Height",Dg="[Amplitude] Viewport Width",uI="[Amplitude] Max Page X",lI="[Amplitude] Max Page Y",Ug="[Amplitude] Page View ID",cI="https://cdn.amplitude.com/libs/visual-tagging-selector-1.0.0-alpha.js.gz",fI="amp-visual-tagging-selector-highlight",Fg="data-amp-mask-attributes",dI=25,Hg=128,hI="AMP_PAGE_VIEW",vI=18e3,pI=["input","select","textarea"],Bg=function(e,t){var r,n=(r=window?.getComputedStyle)===null||r===void 0?void 0:r.call(window,e);return n?.getPropertyValue("cursor")==="pointer"&&t==="click"},jg=function(e){var t=e.pageUrlAllowlist,r=e.pageUrlExcludelist;return!(r&&r.length>0&&Pu(window.location.href,r)||!Pu(window.location.href,t))},Bn=function(e,t,r){return r===void 0&&(r=!1),function(n,i){var o,s,a=e.shouldTrackEventResolver,u=(s=(o=i?.tagName)===null||o===void 0?void 0:o.toLowerCase)===null||s===void 0?void 0:s.call(o);if(!u)return!1;if(a)return a(n,i);if(!jg(e))return!1;var c=String(i?.getAttribute("type"))||"";if(typeof c=="string")switch(c.toLowerCase()){case"hidden":return!1;case"password":return!1}var l=Bg(i,n);if(r&&l)return!0;if(t){var f=t.some(function(d){var h;return!!(!((h=i?.matches)===null||h===void 0)&&h.call(i,d))});if(!f)return!1}switch(u){case"input":case"select":case"textarea":return n==="change"||n==="click";default:return l?!0:n==="click"}}},gI=function(e){var t,r,n,i=(r=(t=e?.tagName)===null||t===void 0?void 0:t.toLowerCase)===null||r===void 0?void 0:r.call(t),o=e instanceof HTMLElement?((n=e.getAttribute("contenteditable"))===null||n===void 0?void 0:n.toLowerCase())==="true":!1;return!pI.includes(i)&&!o},mI=function(e){return e?e.split(",").map(function(t){return t.trim()}).filter(function(t){return t.length>0&&t!=="id"&&t!=="class"}):[]},yI=function(e,t){return Object.entries(e).reduce(function(r,n){var i=oe(n,2),o=i[0],s=i[1];if(o.startsWith(t)){var a=o.replace(t,"");a&&(r[a]=s||"")}return r},{})},bI=function(e){return e==null||typeof e=="object"&&Object.keys(e).length===0||typeof e=="string"&&e.trim().length===0},bd=function(e){return Object.keys(e).reduce(function(t,r){var n=e[r];return bI(n)||(t[r]=n),t},{})},qg=function(){var e;try{var t=le(),r=(e=t?.sessionStorage)===null||e===void 0?void 0:e.getItem(hI);if(!r)return;var n=JSON.parse(r);if(typeof n.pageViewId=="string")return n.pageViewId}catch{}},Vl=function(e,t){return e?t.some(function(r){var n;return(n=e?.matches)===null||n===void 0?void 0:n.call(e,r)})?e:Vl(e?.parentElement,t):null},io=function(e){return!(e.event.target===null||!e.closestTrackedAncestor)};function EI(e){return e.type==="click"||e.type==="change"}var Ss;(function(e){e[e.LEFT_OR_TOUCH_CONTACT=0]="LEFT_OR_TOUCH_CONTACT",e[e.MIDDLE=1]="MIDDLE",e[e.RIGHT=2]="RIGHT"})(Ss||(Ss={}));var Ed="__AMPLITUDE_VISUAL_TAGGING__";function _I(e,t){var r=e;if(r[Ed]!==!0){r[Ed]=!0;var n=t.dataExtractor,i=t.isElementSelectable,o=t.cssSelectorAllowlist,s=t.actionClickAllowlist,a=null,u=function(l){e.notify({action:"element-selected",data:l})},c=function(l,f){l==="selector-mode-changed"?e.notify({action:"track-selector-mode-changed",data:f}):l==="selector-moved"&&e.notify({action:"track-selector-moved",data:f})};e.registerActionHandler("initialize-visual-tagging-selector",function(l){e.loadScriptOnce(cI).then(function(){var f;a=(f=window?.amplitudeVisualTaggingSelector)===null||f===void 0?void 0:f.call(window,{getEventTagProps:n.getEventTagProps,isElementSelectable:function(d){return i?i(l?.actionType||"click",d):!0},onTrack:c,onSelect:u,visualHighlightClass:fI,messenger:e,cssSelectorAllowlist:o,actionClickAllowlist:s,extractDataFromDataSource:n.extractDataFromDataSource,dataExtractor:n,diagnostics:{autocapture:{version:Ng}}}),e.notify({action:"selector-loaded"})}).catch(function(){var f;(f=e.logger)===null||f===void 0||f.warn("Failed to initialize visual tagging selector")})}),e.registerActionHandler("close-visual-tagging-selector",function(){var l;(l=a?.close)===null||l===void 0||l.call(a)})}}function SI(e){var t=e.amplitude,r=e.allObservables,n=e.shouldTrackEvent,i=e.evaluateTriggers,o=r.clickObservable,s=o.filter(io).filter(function(u){return n("click",u.closestTrackedAncestor)}).map(function(u){return i(u)}),a=s;return a.subscribe(function(u){t?.track(Lg,u.targetElementProperties)})}function wI(e){var t=e.amplitude,r=e.allObservables,n=e.getEventProperties,i=e.shouldTrackEvent,o=e.evaluateTriggers,s=r.changeObservable,a=s.filter(io).filter(function(u){return i("change",u.closestTrackedAncestor)}).map(function(u){return o(u)});return a.subscribe(function(u){t?.track(XC,n("change",u.closestTrackedAncestor))})}function TI(e){var t=e.amplitude,r=e.allObservables,n=e.options,i=e.getEventProperties,o=e.shouldTrackEvent,s=e.shouldTrackActionClick,a=r.clickObservable,u=r.mutationObservable,c=r.navigateObservable,l=a.filter(function(b){return!o("click",b.closestTrackedAncestor)}).map(function(b){var E=Vl(b.event.target,n.actionClickAllowlist);return b.closestTrackedAncestor=E,b.closestTrackedAncestor!==null&&(b.targetElementProperties=i(b.type,b.closestTrackedAncestor)),b}).filter(io).filter(function(b){return s("click",b.closestTrackedAncestor)}),f=c?cn(u,c):u,d=cn(l,f),h=null,v=null,p=jl(d,function(b){if(h&&(clearTimeout(h),h=null),b.type==="click")return v=b,h=setTimeout(function(){h=null,v=null},500),Promise.resolve(null);if(v){var E=v;return v=null,Promise.resolve(E)}return Promise.resolve(null)});return p.subscribe(function(b){b&&t?.track(Lg,i("click",b.closestTrackedAncestor))})}function AI(e){var t=e.allObservables,r=t.scrollObservable,n={maxX:0,maxY:0},i=r.subscribe(function(){var o,s,a,u,c=le(),l=Math.floor((s=(o=c?.scrollX)!==null&&o!==void 0?o:c?.pageXOffset)!==null&&s!==void 0?s:0),f=Math.floor((u=(a=c?.scrollY)!==null&&a!==void 0?a:c?.pageYOffset)!==null&&u!==void 0?u:0);n.maxX=Math.max(n.maxX,l),n.maxY=Math.max(n.maxY,f)});return{unsubscribe:function(){i.unsubscribe()},getState:function(){return n},reset:function(){n.maxX=0,n.maxY=0}}}var Qn=le(),Vg=function(){return new tt(function(e){var t=new MutationObserver(function(r){e.next(r)});return document.body&&t.observe(document.body,{childList:!0,attributes:!0,characterData:!0,subtree:!0}),function(){return t.disconnect()}})},$g=function(e){return e===void 0&&(e="click"),new tt(function(t){var r,n=function(i){t.next(i)};return(r=le())===null||r===void 0||r.document.addEventListener(e,n,{capture:!0}),function(){var i;(i=le())===null||i===void 0||i.document.removeEventListener(e,n,{capture:!0})}})},CI=function(){return new tt(function(e){var t,r=function(n){e.next(n)};return(t=le())===null||t===void 0||t.addEventListener("scroll",r),function(){var n;(n=le())===null||n===void 0||n.removeEventListener("scroll",r)}})},II=function(){return new tt(function(e){var t=function(r){for(var n=[],i=1;i ")},KI=function(e,t,r){var n,i;if(e.nodeType!==Node.ELEMENT_NODE)return null;var o=e.getAttribute("id");if(t){if(o)return new di(l(o),!0);var s=e.tagName.toLowerCase();if(s==="body"||s==="head"||s==="html")return new di(s,!0)}var a=e.tagName.toLowerCase();if(o)return new di(a+l(o),!0);var u=e.parentNode;if(!u||u.nodeType===Node.DOCUMENT_NODE)return new di(a,!0);function c(R){var k=R.getAttribute("class");return k?k.split(/\s+/g).filter(Boolean).map(function(N){return"$"+N}):[]}function l(R){return"#"+CSS.escape(R)}for(var f=c(e),d=!1,h=!1,v=-1,p=-1,b=u.children,E=0;b&&(v===-1||!h)&&E=0;_--){var S=g[_];if(S){var A=mI(S.getAttribute(Fg)),C=_===g.length-1?[]:(p=y.get(g[_+1]))!==null&&p!==void 0?p:new Set,I=new Set(Ee(Ee([],oe(C),!1),oe(A),!1));y.set(S,I)}}m=g.map(function(ee){var B;return $I(ee,(B=y.get(ee))!==null&&B!==void 0?B:new Set)});var P=function(ee){ee?.attrs&&Object.entries(ee.attrs).forEach(function(B){var z=oe(B,2),K=z[0],Q=z[1];ee.attrs&&(ee.attrs[K]=o.replaceSensitiveString(Q))})};try{for(var R=ce(m),k=R.next();!k.done;k=R.next()){var N=k.value;P(N)}}catch(ee){h={error:ee}}finally{try{k&&!k.done&&(v=R.return)&&v.call(R)}finally{if(h)throw h.error}}var $=performance.now();return(b=o.diagnosticsClient)===null||b===void 0||b.recordHistogram("autocapturePlugin.getHierarchy",$-E),m},this.getNearestLabel=function(d){var h=d.parentElement;if(!h)return"";var v;try{v=h.querySelector(":scope>span,h1,h2,h3,h4,h5,h6")}catch{v=null}return v?o.getText(v):o.getNearestLabel(h)},this.getElementPath=function(d){var h;if(!d)return"";var v=performance.now(),p=GI(d),b=performance.now();return(h=o.diagnosticsClient)===null||h===void 0||h.recordHistogram("autocapturePlugin.getElementPath",b-v),p},this.getEventProperties=function(d,h,v){var p,b,E,m,g=(E=(b=h?.tagName)===null||b===void 0?void 0:b.toLowerCase)===null||E===void 0?void 0:E.call(b),y=typeof h.getBoundingClientRect=="function"?h.getBoundingClientRect():{left:null,top:null},_=o.getHierarchy(h),S=(m=_[0])===null||m===void 0?void 0:m.attrs,A=o.getNearestLabel(h),C=yI(S??{},v),I=(p={},p[ZC]=_,p[md]=g,p[yd]=o.getText(h),p[tI]=y.left==null?null:Math.round(y.left),p[rI]=y.top==null?null:Math.round(y.top),p[iI]=C,p[oI]=o.getElementPath(h),p[sI]=A,p[Lu]=Lr(window.location.href.split("?")[0]),p[aI]=Bl(o.replaceSensitiveString),p[Mg]=window.innerHeight,p[Dg]=window.innerWidth,p),P=qg();if(P&&(I[Ug]=P),I[JC]=h.getAttribute("id")||"",I[QC]=h.getAttribute("class"),I[nI]=S?.["aria-label"],g==="a"&&d==="click"&&h instanceof HTMLAnchorElement){var R=h.href.substring(0,Hg);I[eI]=o.replaceSensitiveString(R)}return bd(I)},this.addTypeAndTimestamp=function(d,h){return{event:d,timestamp:Date.now(),type:h}},this.addAdditionalEventProperties=function(d,h,v,p,b){b===void 0&&(b=!1);var E=o.addTypeAndTimestamp(d,h);if(EI(E)&&E.event.target!==null){if(b){var m=Bg(E.event.target,E.type);if(m)return E.closestTrackedAncestor=E.event.target,E.targetElementProperties=o.getEventProperties(E.type,E.closestTrackedAncestor,p),E}var g=Vl(E.event.target,v);return g&&(E.closestTrackedAncestor=g,E.targetElementProperties=o.getEventProperties(E.type,g,p)),E}return E},this.extractDataFromDataSource=function(d,h){if(d.sourceType==="DOM_ELEMENT"){var v=LI(d,h);return v?d.elementExtractType==="TEXT"?o.getText(v):d.elementExtractType==="ATTRIBUTE"&&d.attribute?v.getAttribute(d.attribute):void 0:void 0}},this.getTextWithMaskedDescendants=function(d){var h,v,p="[".concat(In,"], [contenteditable]");if(!d.querySelector(p))return d.innerText;var b="",E=Array.from(d.childNodes);try{for(var m=ce(E),g=m.next();!g.done;g=m.next()){var y=g.value;if(y.nodeType===Node.TEXT_NODE){b+=y.textContent||"";continue}if(y instanceof Element){if(y.hasAttribute(In)||y.hasAttribute("contenteditable")){b+=Mr;continue}b+=o.getTextWithMaskedDescendants(y)}}}catch(_){h={error:_}}finally{try{g&&!g.done&&(v=m.return)&&v.call(m)}finally{if(h)throw h.error}}return b},this.getText=function(d){var h=d.closest("[".concat(In,"]"))!==null;if(h)return Mr;var v="";return d.querySelector("[".concat(In,"], [contenteditable]"))?v=o.getTextWithMaskedDescendants(d):v=d.innerText||"",o.replaceSensitiveString(v.substring(0,255)).replace(/\s+/g," ").trim()},this.getEventTagProps=function(d){var h,v,p;if(!d)return{};var b=(p=(v=d?.tagName)===null||v===void 0?void 0:v.toLowerCase)===null||p===void 0?void 0:p.call(v),E=(h={},h[md]=b,h[yd]=o.getText(d),h[Lu]=window.location.href.split("?")[0],h);return bd(E)},this.diagnosticsClient=r?.diagnosticsClient;var a=(s=t.maskTextRegex)!==null&&s!==void 0?s:[],u=[];try{for(var c=ce(a),l=c.next();!l.done;l=c.next()){var f=l.value;if(u.length>=dI)break;if(f instanceof RegExp)u.push(f);else if("pattern"in f&&typeof f.pattern=="string")try{u.push(new RegExp(f.pattern,"i"))}catch{}}}catch(d){n={error:d}}finally{try{l&&!l.done&&(i=c.return)&&i.call(c)}finally{if(n)throw n.error}}this.additionalMaskTextPatterns=u}return e})();function zI(e){var t=e.allObservables,r=e.onExposure,n=e.dataExtractor,i=e.exposureDuration,o=i===void 0?sg:i,s=new Map,a=new Map,u=t.exposureObservable,c=u.subscribe(function(l){var f=l,d=f.target;if(f.isIntersecting){if(!s.get(d)){var h=setTimeout(function(){s.set(d,!0);var v=n.getElementPath(d);r(v),a.set(d,null)},o);a.set(d,h)}}else if(!f.isIntersecting&&f.intersectionRatio<1){var h=a.get(d);h&&(clearTimeout(h),a.set(d,null))}});return{unsubscribe:function(){c.unsubscribe()},reset:function(){a.forEach(function(l){l&&clearTimeout(l)}),a.clear(),s.clear()}}}function XI(e){var t,r,n,i,o=e.amplitude,s=e.scrollTracker,a=e.currentElementExposed,u=e.elementExposedForPage,c=e.exposureTracker,l=e.isPageEnd,f=e.lastScroll,d=s.getState(),h=le(),v=(r=h?.innerWidth)!==null&&r!==void 0?r:0,p=(n=h?.innerHeight)!==null&&n!==void 0?n:0,b=(t={},t[Lu]=(i=h?.location)===null||i===void 0?void 0:i.href,t[uI]=d.maxX+v,t[lI]=d.maxY+p,t[Mg]=p,t[Dg]=v,t["[Amplitude] Element Exposed"]=Array.from(a),t),E=qg();if(E&&(b[Ug]=E),a.size===0&&d.maxX===f.maxX&&d.maxY===f.maxY){l&&(s.reset(),u.clear(),c?.reset());return}o?.track("[Amplitude] Viewport Content Updated",b),f.maxX=d.maxX,f.maxY=d.maxY,a.clear(),l&&(s.reset(),u.clear(),c?.reset())}function YI(e,t,r,n){if(!t.has(e)){t.add(e),r.add(e);var i=Array.from(r),o=JSON.stringify(i);o.length>=vI&&n(!1)}}var vt;(function(e){e.ClickObservable="clickObservable",e.ChangeObservable="changeObservable",e.NavigateObservable="navigateObservable",e.MutationObservable="mutationObservable",e.ScrollObservable="scrollObservable",e.ExposureObservable="exposureObservable",e.BrowserErrorObservable="browserErrorObservable",e.SelectionObservable="selectionObservable",e.MouseMoveObservable="mouseMoveObservable"})(vt||(vt={}));var JI=function(e,t){var r,n,i,o,s,a,u,c,l,f,d,h;e===void 0&&(e={}),t?.diagnosticsClient.setTag("plugin.autocapture.version",Ng);var v=e.dataAttributePrefix,p=v===void 0?Ul:v,b=e.visualTaggingOptions,E=b===void 0?{enabled:!0}:b;e.cssSelectorAllowlist=(r=e.cssSelectorAllowlist)!==null&&r!==void 0?r:ig,e.actionClickAllowlist=(n=e.actionClickAllowlist)!==null&&n!==void 0?n:og,e.debounceTime=(i=e.debounceTime)!==null&&i!==void 0?i:0;var m=((o=e.viewportContentUpdated)===null||o===void 0?void 0:o.enabled)!==!1,g=(u=(a=(s=e.viewportContentUpdated)===null||s===void 0?void 0:s.exposureDuration)!==null&&a!==void 0?a:e.exposureDuration)!==null&&u!==void 0?u:sg;e.viewportContentUpdated=D(D({},e.viewportContentUpdated),{exposureDuration:g}),e.pageUrlExcludelist=(c=e.pageUrlExcludelist)===null||c===void 0?void 0:c.reduce(function(Q,ae){if(typeof ae=="string"&&Q.push(ae),ae instanceof RegExp&&Q.push(ae),typeof ae=="object"&&ae!==null&&"pattern"in ae)try{Q.push(new RegExp(ae.pattern))}catch(fe){return console.warn("Invalid regex pattern: ".concat(ae.pattern),fe),Q}return Q},[]);var y=$C,_="enrichment",S=[],A=new Wg(e,t),C=new Set,I=new Set,P,R=function(){var Q,ae=Xt($g().map(function(me){return A.addAdditionalEventProperties(me,"click",e.cssSelectorAllowlist,p)})),fe=Xt(new tt(function(me){var M,Z=function(X){var re=A.addAdditionalEventProperties(X,"change",e.cssSelectorAllowlist,p);me.next(re)};return(M=le())===null||M===void 0||M.document.addEventListener("change",Z,{capture:!0}),function(){var X;return(X=le())===null||X===void 0?void 0:X.document.removeEventListener("change",Z)}})),pe;window.navigation&&(pe=Xt(new tt(function(me){var M=function(Z){var X=A.addAdditionalEventProperties(Z,"navigate",e.cssSelectorAllowlist,p);me.next(X)};return window.navigation.addEventListener("navigate",M),function(){window.navigation.removeEventListener("navigate",M)}})));var ye=Xt(Vg().map(function(me){return A.addAdditionalEventProperties(me,"mutation",e.cssSelectorAllowlist,p)})),be=CI(),de=kI(ye,e.cssSelectorAllowlist);return Q={},Q[vt.ChangeObservable]=fe,Q[vt.ClickObservable]=ae,Q[vt.MutationObservable]=ye,Q[vt.NavigateObservable]=pe,Q[vt.ScrollObservable]=be,Q[vt.ExposureObservable]=de,Q},k=_d(Object.values((f=(l=e.pageActions)===null||l===void 0?void 0:l.labeledEvents)!==null&&f!==void 0?f:{})),N=Sd((h=(d=e.pageActions)===null||d===void 0?void 0:d.triggers)!==null&&h!==void 0?h:[]),$=HI(k,N,A,e),ee=function(Q){var ae,fe;Q&&(e.pageActions=D(D({},e.pageActions),Q),k=_d(Object.values((ae=e.pageActions.labeledEvents)!==null&&ae!==void 0?ae:{})),N=Sd((fe=e.pageActions.triggers)!==null&&fe!==void 0?fe:[]),$.update(k,N,e))},B=function(Q,ae){return x(void 0,void 0,void 0,function(){var fe,pe,ye,be,de,me,M,Z,X,re,F,ge,w,T,L,H,U,q,te,G,Y;return O(this,function(V){return typeof document>"u"?[2]:(fe=!1,pe={maxX:void 0,maxY:void 0},Q.fetchRemoteConfig&&(Q.remoteConfigClient?Q.remoteConfigClient.subscribe("configs.analyticsSDK.pageActions","all",function(W){ee(W)}):Q.loggerProvider.debug("Remote config client is not provided, skipping remote config fetch")),ye=Bn(e,e.cssSelectorAllowlist),be=Bn(e,e.actionClickAllowlist),de=R(),me=SI({allObservables:de,amplitude:ae,shouldTrackEvent:ye,evaluateTriggers:$.evaluate.bind($)}),S.push(me),M=wI({allObservables:de,getEventProperties:function(){for(var W=[],j=0;jwd||e.xMax-e.xMin>wd}function Ad(e){if(e.length===0)return null;var t=e[0],r=e[e.length-1],n=D({"[Amplitude] Begin Time":new Date(t.timestamp).toISOString(),"[Amplitude] End Time":new Date(r.timestamp).toISOString(),"[Amplitude] Duration":r.timestamp-t.timestamp,"[Amplitude] Clicks":e.map(function(i){return{X:i.event.pageX,Y:i.event.pageY,Time:i.timestamp}}),"[Amplitude] Click Count":e.length},t.targetElementProperties);return{rageClickEvent:n,time:t.timestamp}}function tk(e,t){var r=Math.max(0,e.length-Gg+1),n=e[r];return t.timestamp-n.timestamp>=Kg}function rk(e,t){return e.length>0&&e[e.length-1].closestTrackedAncestor!==t.closestTrackedAncestor}function nk(e){var t=this,r=e.amplitude,n=e.allObservables,i=e.shouldTrackRageClick,o=n.clickObservable,s=n.selectionObservable,a=[],u={},c=null;function l(v){a=[],u={},v&&(Td(u,v),a.push(v))}var f=jl(o.filter(function(v){return i("click",v.closestTrackedAncestor)}),function(v){return x(t,void 0,void 0,function(){var p;return O(this,function(b){return Td(u,v),p=null,a.length===0||rk(a,v)||tk(a,v)||u.isOutOfBounds?(c&&(p=Ad(a)),l(v)):a.push(v),c&&(clearTimeout(c.timerId),c.resolve(p),c=null),a.length>=Gg?[2,new Promise(function(E){c={resolve:E,timerId:setTimeout(function(){E(Ad(a))},Kg)}})]:[2,null]})})}),d=s?.subscribe(function(){l()}),h=f.subscribe(function(v){v!==null&&r.track(KC,v.rageClickEvent,{time:v.time})});return{unsubscribe:function(){h.unsubscribe(),d?.unsubscribe()}}}var ik=2e3;function ok(e){var t=e.amplitude,r=e.allObservables,n=e.shouldTrackErrorClick,i=r.clickObservable,o=r.browserErrorObservable,s=i.filter(function(l){return io(l)&&n("click",l.closestTrackedAncestor)&&l.event.target instanceof Element&&l.event.target.closest('a[target="_blank"]')===null&&l.event.button===Ss.LEFT_OR_TOUCH_CONTACT}),a=null,u=null,c=function(){a!==null&&(clearTimeout(a),a=null),u=null};return cn(s,o).subscribe(function(l){var f;if(l.type==="click"){c(),u=l,a=setTimeout(c,ik);return}l.type==="error"&&u&&(t.track(zC,D((f={},f["[Amplitude] Kind"]=l.event.kind,f["[Amplitude] Message"]=l.event.message,f["[Amplitude] Stack"]=l.event.stack,f["[Amplitude] Filename"]=l.event.filename,f["[Amplitude] Line Number"]=l.event.lineNumber,f["[Amplitude] Column Number"]=l.event.columnNumber,f),u.targetElementProperties)),c())})}var Kt;(function(e){e.INCREASING="increasing",e.DECREASING="decreasing"})(Kt||(Kt={}));var en;(function(e){e.X="x",e.Y="y"})(en||(en={}));var sk=function(e){var t=e.allWindowObservables,r=t.mouseMoveObservable;return new tt(function(n){var i=null,o=null,s=null;return r.subscribe(function(a){var u={x:a.clientX,y:a.clientY};if(i===null){i=u;return}u.x>i.x?(o===Kt.DECREASING&&n.next(en.X),o=Kt.INCREASING):u.xi.y?(s===Kt.DECREASING&&n.next(en.Y),s=Kt.INCREASING):u.yn&&r.shift()}function Cd(e){var t=e.changes,r=e.changesThreshold,n=e.thresholdMs;if(t.length"u"?[2]:(_=p(),a&&(S=Bn(e,c),A=nk({allObservables:_,amplitude:y,shouldTrackRageClick:S}),i.push(A)),s&&(C=Bn(e,l),I=ek({amplitude:y,allObservables:_,getEventProperties:function(z,K){return h.getEventProperties(z,K,d)},shouldTrackDeadClick:C}),i.push(I)),o&&(P=Bn(e,f),R=ok({amplitude:y,allObservables:_,shouldTrackErrorClick:P}),i.push(R)),u&&(k=void 0,N=void 0,typeof e.thrashedCursor=="object"&&(k=e.thrashedCursor.directionChanges,N=e.thrashedCursor.threshold,k&&kAo&&(g.loggerProvider.warn("'thrashedCursor.threshold' of ".concat(N," is above the maximum of ").concat(Ao,", setting to ").concat(Ao)),N=Ao)),$=ck({amplitude:y,options:e,allObservables:_,directionChanges:k,thresholdMs:N}),i.push($)),(ee=g?.loggerProvider)===null||ee===void 0||ee.log("".concat(r," has been successfully added.")),[2])})})},E=function(g){return x(void 0,void 0,void 0,function(){return O(this,function(y){return[2,g]})})},m=function(){return x(void 0,void 0,void 0,function(){var g,y,_,S,A;return O(this,function(C){try{for(g=ce(i),y=g.next();!y.done;y=g.next())_=y.value,_.unsubscribe()}catch(I){S={error:I}}finally{try{y&&!y.done&&(A=g.return)&&A.call(g)}finally{if(S)throw S.error}}return[2]})})};return{name:r,type:n,setup:b,execute:E,teardown:m}},dk="@amplitude/plugin-network-capture-browser",Yg="[Amplitude] Network Request",Jg="500-599";function Wo(e,t){var r=t.replace(/[-[\]{}()+?.,\\^$|#\s]/g,"\\$&"),n="^"+r.replace(/\*/g,".*")+"$",i=new RegExp(n);return i.test(e)}function Qg(e,t){var r,n,i=t.split(",");try{for(var o=ce(i),s=o.next();!s.done;s=o.next()){var a=s.value,u=oe(a.split("-").map(Number),2),c=u[0],l=u[1];if(e===c&&l===void 0||e>=c&&e<=l)return!0}}catch(f){r={error:f}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return!1}function hk(e,t,r,n,i){if(!(e.hosts&&!e.hosts.find(function(s){return Wo(t,s)}))&&!(n&&e.urls&&!Pu(n,e.urls))&&!(i&&e.methods&&!e.methods.find(function(s){return i.toLowerCase()===s.toLowerCase()||s==="*"}))){if(r||r===0){var o=e.statusCodeRange||Jg;if(!Qg(r,o))return!1}return!0}}function Zg(e){var t;if(e)try{var r=(t=le())===null||t===void 0?void 0:t.location.href,n=new URL(e,r),i=n.searchParams.toString(),o=n.hash.replace("#",""),s=n.href,a=n.host;n.hash="",n.search="";var u=n.href;return{query:i,fragment:o,href:s,hrefWithoutQueryOrHash:u,host:a}}catch{return}}function vk(e,t){if(e.includes("amplitude.com"))try{var r=t.body;if(typeof r!="string")return!1;var n=JSON.parse(r),i=n.events;if(i.find(function(o){return o.event_type===Yg}))return!0}catch{}return!1}function Pd(e){if(typeof e!="object"||e===null){if(e)return Ee([],oe(ms),!1);if(e===void 0){var t=void 0;return t}return}if(e.length!==0)return e}function Rd(e){var t,r;return!(!((t=e?.allowlist)===null||t===void 0)&&t.length)&&!(!((r=e?.blocklist)===null||r===void 0)&&r.length)}function pk(e,t){var r;t===void 0&&(t={});var n=Zg(e.url);if(!n)return!1;var i=n.host;if(t.ignoreAmplitudeRequests!==!1&&(Wo(i,"*.amplitude.com")||Wo(i,"amplitude.com"))||!((r=t.ignoreHosts)===null||r===void 0)&&r.find(function(s){return Wo(i,s)})||!t.captureRules&&e.status!==void 0&&!Qg(e.status,Jg))return!1;if(t.captureRules){var o;if(Ee([],oe(t.captureRules),!1).reverse().find(function(s){if(o=hk(s,i,e.status,e.url,e.method),o){var a=Pd(s.responseHeaders);if(e.responseWrapper&&a){var u=e.responseWrapper.headers(a);u&&(e.responseHeaders=u)}var c=Pd(s.requestHeaders);if(e.requestWrapper&&c){var l=e.requestWrapper.headers(c);l&&(e.requestHeaders=l)}e.responseWrapper&&s.responseBody&&!Rd(s.responseBody)&&(e.responseBodyJson=e.responseWrapper.json(s.responseBody.allowlist,s.responseBody.blocklist)),e.requestWrapper&&s.requestBody&&!Rd(s.requestBody)&&(e.requestBodyJson=e.requestWrapper.json(s.requestBody.allowlist,s.requestBody.blocklist))}return o!==void 0}),!o)return!1}return!(e.requestWrapper&&vk(i,e.requestWrapper))}function gk(e,t,r,n){return x(this,void 0,void 0,function(){var i,o,s;return O(this,function(a){switch(a.label){case 0:return t.requestBodyJson||t.responseBodyJson?[4,Promise.all([t.requestBodyJson,t.responseBodyJson])]:[3,2];case 1:if(i=oe.apply(void 0,[a.sent(),2]),o=i[0],s=i[1],o)try{e["[Amplitude] Request Body"]=JSON.stringify(o)}catch(u){n?.debug("Failed to stringify request body",u)}if(s)try{e["[Amplitude] Response Body"]=JSON.stringify(s)}catch{n?.debug("Failed to stringify response body")}a.label=2;case 2:return r?.track(Yg,e),[2]}})})}function mk(e){var t=e.allObservables,r=e.networkTrackingOptions,n=e.amplitude,i=e.loggerProvider,o=t.networkObservable,s=o.filter(function(a){return pk(a.event,r)});return s.subscribe(function(a){var u,c,l,f=a.event,d=Zg(f.url);if(d){var h=(c=f.responseWrapper)===null||c===void 0?void 0:c.bodySize,v=(l=f.requestWrapper)===null||l===void 0?void 0:l.bodySize,p=(u={},u["[Amplitude] URL"]=d.hrefWithoutQueryOrHash,u["[Amplitude] URL Query"]=d.query,u["[Amplitude] URL Fragment"]=d.fragment,u["[Amplitude] Request Method"]=f.method,u["[Amplitude] Status Code"]=f.status,u["[Amplitude] Start Time"]=f.startTime,u["[Amplitude] Completion Time"]=f.endTime,u["[Amplitude] Duration"]=f.duration,u["[Amplitude] Request Body Size"]=v,u["[Amplitude] Response Body Size"]=h,u["[Amplitude] Request Type"]=f.type,u["[Amplitude] Request Headers"]=f.requestHeaders,u["[Amplitude] Response Headers"]=f.responseHeaders,u);gk(p,f,n,i)}})}var Du;(function(e){e.NetworkObservable="networkObservable"})(Du||(Du={}));var Od,yk=function(e){e===void 0&&(e={});var t=dk,r="enrichment",n,i=function(c,l){var f={event:c,timestamp:Date.now(),type:l};return f},o=function(){var c,l=new tt(function(f){var d=new OA(function(h){var v=i(h,"network");f.next(v)});return ed.subscribe(d,n),function(){ed.unsubscribe(d)}});return c={},c[Du.NetworkObservable]=l,c},s=function(c,l){return x(void 0,void 0,void 0,function(){var f;return O(this,function(d){return typeof document>"u"?[2]:(f=o(),n=c?.loggerProvider,Od=mk({allObservables:f,networkTrackingOptions:e,amplitude:l,loggerProvider:n}),n?.log("".concat(t," has been successfully added.")),[2])})})},a=function(c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return[2,c]})})},u=function(){return x(void 0,void 0,void 0,function(){return O(this,function(c){return Od.unsubscribe(),[2]})})};return{name:t,type:r,setup:s,execute:a,teardown:u}},bk="web-vitals-browser",Ek="[Amplitude] Web Vitals";let em=-1;const ri=e=>{addEventListener("pageshow",(t=>{t.persisted&&(em=t.timeStamp,e(t))}),!0)},er=(e,t,r,n)=>{let i,o;return s=>{t.value>=0&&(s||n)&&(o=t.value-(i??0),(o||i===void 0)&&(i=t.value,t.delta=o,t.rating=((a,u)=>a>u[1]?"poor":a>u[0]?"needs-improvement":"good")(t.value,r),e(t)))}},$l=e=>{requestAnimationFrame((()=>requestAnimationFrame((()=>e()))))},Wl=()=>{const e=performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStartWl()?.activationStart??0,tr=(e,t=-1)=>{const r=Wl();let n="navigate";return em>=0?n="back-forward-cache":r&&(document.prerendering||oo()>0?n="prerender":document.wasDiscarded?n="restore":r.type&&(n=r.type.replace(/_/g,"-"))),{name:e,value:t,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:n}},La=new WeakMap;function Gl(e,t){return La.get(e)||La.set(e,new t),La.get(e)}class _k{t;i=0;o=[];h(t){if(t.hadRecentInput)return;const r=this.o[0],n=this.o.at(-1);this.i&&r&&n&&t.startTime-n.startTime<1e3&&t.startTime-r.startTime<5e3?(this.i+=t.value,this.o.push(t)):(this.i=t.value,this.o=[t]),this.t?.(t)}}const so=(e,t,r={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(e)){const n=new PerformanceObserver((i=>{Promise.resolve().then((()=>{t(i.getEntries())}))}));return n.observe({type:e,buffered:!0,...r}),n}}catch{}},Kl=e=>{let t=!1;return()=>{t||(e(),t=!0)}};let kn=-1;const tm=new Set,xd=()=>document.visibilityState!=="hidden"||document.prerendering?1/0:0,Uu=e=>{if(document.visibilityState==="hidden"){if(e.type==="visibilitychange")for(const t of tm)t();isFinite(kn)||(kn=e.type==="visibilitychange"?e.timeStamp:0,removeEventListener("prerenderingchange",Uu,!0))}},$s=()=>{if(kn<0){const e=oo();kn=(document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((r=>r.name==="hidden"&&r.startTime>e))[0]?.startTime)??xd(),addEventListener("visibilitychange",Uu,!0),addEventListener("prerenderingchange",Uu,!0),ri((()=>{setTimeout((()=>{kn=xd()}))}))}return{get firstHiddenTime(){return kn},onHidden(e){tm.add(e)}}},Ws=e=>{document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()},Nd=[1800,3e3],rm=(e,t={})=>{Ws((()=>{const r=$s();let n,i=tr("FCP");const o=so("paint",(s=>{for(const a of s)a.name==="first-contentful-paint"&&(o.disconnect(),a.startTime{i=tr("FCP"),n=er(e,i,Nd,t.reportAllChanges),$l((()=>{i.value=performance.now()-s.timeStamp,n(!0)}))})))}))},Ld=[.1,.25],Sk=(e,t={})=>{const r=$s();rm(Kl((()=>{let n,i=tr("CLS",0);const o=Gl(t,_k),s=u=>{for(const c of u)o.h(c);o.i>i.value&&(i.value=o.i,i.entries=o.o,n())},a=so("layout-shift",s);a&&(n=er(e,i,Ld,t.reportAllChanges),r.onHidden((()=>{s(a.takeRecords()),n(!0)})),ri((()=>{o.i=0,i=tr("CLS",0),n=er(e,i,Ld,t.reportAllChanges),$l((()=>n()))})),setTimeout(n))})))};let nm=0,Ma=1/0,Co=0;const wk=e=>{for(const t of e)t.interactionId&&(Ma=Math.min(Ma,t.interactionId),Co=Math.max(Co,t.interactionId),nm=Co?(Co-Ma)/7+1:0)};let Fu;const Md=()=>Fu?nm:performance.interactionCount??0,Tk=()=>{"interactionCount"in performance||Fu||(Fu=so("event",wk,{type:"event",buffered:!0,durationThreshold:0}))};let Dd=0,Ak=class{u=[];l=new Map;m;p;v(){Dd=Md(),this.u.length=0,this.l.clear()}L(){const t=Math.min(this.u.length-1,Math.floor((Md()-Dd)/50));return this.u[t]}h(t){if(this.m?.(t),!t.interactionId&&t.entryType!=="first-input")return;const r=this.u.at(-1);let n=this.l.get(t.interactionId);if(n||this.u.length<10||t.duration>r.P){if(n?t.duration>n.P?(n.entries=[t],n.P=t.duration):t.duration===n.P&&t.startTime===n.entries[0].startTime&&n.entries.push(t):(n={id:t.interactionId,entries:[t],P:t.duration},this.l.set(n.id,n),this.u.push(n)),this.u.sort(((i,o)=>o.P-i.P)),this.u.length>10){const i=this.u.splice(10);for(const o of i)this.l.delete(o.id)}this.p?.(n)}}};const im=e=>{const t=globalThis.requestIdleCallback||setTimeout;document.visibilityState==="hidden"?e():(e=Kl(e),addEventListener("visibilitychange",e,{once:!0,capture:!0}),t((()=>{e(),removeEventListener("visibilitychange",e,{capture:!0})})))},Ud=[200,500],Ck=(e,t={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const r=$s();Ws((()=>{Tk();let n,i=tr("INP");const o=Gl(t,Ak),s=u=>{im((()=>{for(const l of u)o.h(l);const c=o.L();c&&c.P!==i.value&&(i.value=c.P,i.entries=c.entries,n())}))},a=so("event",s,{durationThreshold:t.durationThreshold??40});n=er(e,i,Ud,t.reportAllChanges),a&&(a.observe({type:"first-input",buffered:!0}),r.onHidden((()=>{s(a.takeRecords()),n(!0)})),ri((()=>{o.v(),i=tr("INP"),n=er(e,i,Ud,t.reportAllChanges)})))}))};let Ik=class{m;h(t){this.m?.(t)}};const Fd=[2500,4e3],kk=(e,t={})=>{Ws((()=>{const r=$s();let n,i=tr("LCP");const o=Gl(t,Ik),s=u=>{t.reportAllChanges||(u=u.slice(-1));for(const c of u)o.h(c),c.startTime{s(a.takeRecords()),a.disconnect(),n(!0)})),c=l=>{l.isTrusted&&(im(u),removeEventListener(l.type,c,{capture:!0}))};for(const l of["keydown","click","visibilitychange"])addEventListener(l,c,{capture:!0});ri((l=>{i=tr("LCP"),n=er(e,i,Fd,t.reportAllChanges),$l((()=>{i.value=performance.now()-l.timeStamp,n(!0)}))}))}}))},Hd=[800,1800],Hu=e=>{document.prerendering?Ws((()=>Hu(e))):document.readyState!=="complete"?addEventListener("load",(()=>Hu(e)),!0):setTimeout(e)},Pk=(e,t={})=>{let r=tr("TTFB"),n=er(e,r,Hd,t.reportAllChanges);Hu((()=>{const i=Wl();i&&(r.value=Math.max(i.responseStart-oo(),0),r.entries=[i],n(!0),ri((()=>{r=tr("TTFB",0),n=er(e,r,Hd,t.reportAllChanges),n(!0)})))}))};function Rk(e){var t,r=((t=e.entries[0])===null||t===void 0?void 0:t.startTime)||0;return performance.timeOrigin+r}function hi(e){return{value:e.value,rating:e.rating,delta:e.delta,navigationType:e.navigationType,id:e.id,timestamp:Math.floor(Rk(e)),navigationStart:Math.floor(performance.timeOrigin)}}var Ok=function(){var e=null,t=le(),r=t?.document,n=t?.location,i=function(a,u){return x(void 0,void 0,void 0,function(){var c,l;return O(this,function(f){return r===void 0?[2]:(c=Lr(n?.href||"",a.loggerProvider),l={"[Amplitude] Page Domain":n?.hostname||"","[Amplitude] Page Location":c,"[Amplitude] Page Path":Lr(n?.pathname||"",a.loggerProvider),"[Amplitude] Page Title":typeof document<"u"&&document.title||"","[Amplitude] Page URL":Lr(c.split("?")[0],a.loggerProvider)},kk(function(d){l["[Amplitude] LCP"]=hi(d)}),rm(function(d){l["[Amplitude] FCP"]=hi(d)}),Ck(function(d){l["[Amplitude] INP"]=hi(d)}),Sk(function(d){l["[Amplitude] CLS"]=hi(d)}),Pk(function(d){l["[Amplitude] TTFB"]=hi(d)}),e=function(){r.visibilityState==="hidden"&&e&&(u.track(Ek,l),r.removeEventListener("visibilitychange",e),e=null)},r.addEventListener("visibilitychange",e),[2])})})},o=function(a){return x(void 0,void 0,void 0,function(){return O(this,function(u){return[2,a]})})},s=function(){return x(void 0,void 0,void 0,function(){return O(this,function(a){return e&&r?.removeEventListener("visibilitychange",e),[2]})})};return{name:bk,type:"enrichment",setup:i,execute:o,teardown:s}},Bd=(function(){function e(t,r){var n;this.shouldTrackNewCampaign=!1,this.options=D({initialEmptyValue:"EMPTY",resetSessionOnNewCampaign:!1,excludeReferrers:CC(((n=r.cookieOptions)===null||n===void 0?void 0:n.domain)||r.topLevelDomain),optOut:r.optOut},t),this.storage=r.cookieStorage,this.storageKey=Gf(r.apiKey,"MKTG"),this.webExpStorageKey=Gf(r.apiKey,"MKTG_ORIGINAL"),this.currentCampaign=js,this.sessionTimeout=r.sessionTimeout,this.lastEventTime=r.lastEventTime,this.logger=r.loggerProvider,this.topLevelDomain=r.topLevelDomain,r.loggerProvider.log("Installing web attribution tracking.")}return e.prototype.init=function(){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:return this.options.optOut?[2]:[4,this.fetchCampaign()];case 1:return r=oe.apply(void 0,[n.sent(),2]),this.currentCampaign=r[0],this.previousCampaign=r[1],t=this.lastEventTime?Xp(this.sessionTimeout,this.lastEventTime):!0,SC(this.currentCampaign,this.previousCampaign,this.options,this.logger,t,this.topLevelDomain)?(this.shouldTrackNewCampaign=!0,[4,this.storage.set(this.storageKey,this.currentCampaign)]):[3,3];case 2:n.sent(),n.label=3;case 3:return[2]}})})},e.prototype.fetchCampaign=function(){return x(this,void 0,void 0,function(){var t;return O(this,function(r){switch(r.label){case 0:return[4,this.storage.get(this.webExpStorageKey)];case 1:return t=r.sent(),t?[4,this.storage.remove(this.webExpStorageKey)]:[3,3];case 2:r.sent(),r.label=3;case 3:return[4,Promise.all([t||new dg().parse(),this.storage.get(this.storageKey)])];case 4:return[2,r.sent()]}})})},e.prototype.generateCampaignEvent=function(t){this.shouldTrackNewCampaign=!1;var r=AC(this.currentCampaign,this.options);return t&&(r.event_id=t),r},e.prototype.shouldSetSessionIdOnNewCampaign=function(){return this.shouldTrackNewCampaign&&!!this.options.resetSessionOnNewCampaign},e})(),Io="AMP_CURRENT_PAGE",ko="AMP_PREVIOUS_PAGE",bn="AMP_URL_INFO",Mi;(function(e){e.Direct="direct",e.Internal="internal",e.External="external"})(Mi||(Mi={}));var xk=new Set([St.IDENTIFY,St.GROUP_IDENTIFY,St.REVENUE]),Nk=function(e){var t={},r=t.internalDomains,n=r===void 0?[]:r,i=le(),o=void 0,s=!1,a=void 0,u=!1,c=!1,l=function(p){var b;try{var E=Lr(p,a);b=new URL(E).hostname}catch(m){a?.error("Could not parse URL: ",m)}return b},f=function(p){var b=typeof location<"u"&&location.hostname||"",E=p?l(p):void 0;if(!E)return Mi.Direct;var m=n.some(function(y){return b.indexOf(y)!==-1}),g=n.some(function(y){return E.indexOf(y)!==-1});return b===E||g&&m?Mi.Internal:Mi.External},d=function(){return x(void 0,void 0,void 0,function(){var p,b,E,m,g;return O(this,function(y){switch(y.label){case 0:return o&&s?[4,o.get(bn)]:[3,3];case 1:return p=y.sent(),b=Lr(typeof location<"u"&&location.href||""),E=p?.[Io]||"",m=void 0,b===E?m=p?.[ko]||"":E?m=E:m=document.referrer||"",[4,o.set(bn,(g={},g[Io]=b,g[ko]=m,g))];case 2:y.sent(),y.label=3;case 3:return[2]}})})},h=function(){d()},v={name:"@amplitude/plugin-page-url-enrichment-browser",type:"enrichment",setup:function(p,b){return x(void 0,void 0,void 0,function(){var E;return O(this,function(m){switch(m.label){case 0:if(a=p.loggerProvider,a.log("Installing @amplitude/plugin-page-url-enrichment-browser"),c=!0,!i)return[3,2];try{o=new qs(i.sessionStorage)}catch{a?.debug("sessionStorage is not available in this environment.")}return[4,o?.isEnabled()];case 1:s=(E=m.sent())!==null&&E!==void 0?E:!1,i.addEventListener("popstate",h),u||(i.history.pushState=new Proxy(i.history.pushState,{apply:function(g,y,_){var S=oe(_,3),A=S[0],C=S[1],I=S[2];g.apply(y,[A,C,I]),c&&h()}}),i.history.replaceState=new Proxy(i.history.replaceState,{apply:function(g,y,_){var S=oe(_,3),A=S[0],C=S[1],I=S[2];g.apply(y,[A,C,I]),c&&h()}}),u=!0),m.label=2;case 2:return[2]}})})},execute:function(p){return x(void 0,void 0,void 0,function(){var b,E,m,g,y;return O(this,function(_){switch(_.label){case 0:return b=Lr(typeof location<"u"&&location.href||""),o&&s?[4,o.get(bn)]:[3,5];case 1:return E=_.sent(),E?.[Io]?[3,3]:[4,o.set(bn,(y={},y[Io]=b,y[ko]=document.referrer||"",y))];case 2:_.sent(),_.label=3;case 3:return[4,o.get(bn)];case 4:if(m=_.sent(),g="",m&&(g=m[ko]||""),xk.has(p.event_type))return[2,p];p.event_properties=D(D({},p.event_properties||{}),{"[Amplitude] Page Domain":vi(p,"[Amplitude] Page Domain",typeof location<"u"&&location.hostname||""),"[Amplitude] Page Location":vi(p,"[Amplitude] Page Location",b),"[Amplitude] Page Path":vi(p,"[Amplitude] Page Path",typeof location<"u"&&Lr(location.pathname)||""),"[Amplitude] Page Title":vi(p,"[Amplitude] Page Title",Bl(Hl)),"[Amplitude] Page URL":vi(p,"[Amplitude] Page URL",b.split("?")[0]),"[Amplitude] Previous Page Location":g,"[Amplitude] Previous Page Type":f(g)}),_.label=5;case 5:return[2,p]}})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(p){switch(p.label){case 0:return i&&(i.removeEventListener("popstate",h),c=!1),o&&s?[4,o.set(bn,{})]:[3,2];case 1:p.sent(),p.label=2;case 2:return[2]}})})}};return v};function vi(e,t,r){return e.event_properties||(e.event_properties={}),e.event_properties[t]===void 0?r:e.event_properties[t]}var Lk=function(){var e,t,r;function n(s){return typeof s!="object"||s===null?!1:"body"in s&&typeof s.body=="string"}function i(s){if(s)try{var a=new Function("return "+s)();if(typeof a=="function")return a;e?.error("Custom enrichment body did not evaluate to a function")}catch(u){e?.error("Could not create custom enrichment function",u)}return function(u){return u}}var o={name:"@amplitude/plugin-custom-enrichment-browser",type:"enrichment",setup:function(s,a){return x(void 0,void 0,void 0,function(){var u,c;return O(this,function(l){return e=s.loggerProvider,e?.log("Installing @amplitude/plugin-custom-enrichment-browser"),!((c=s.remoteConfig)===null||c===void 0)&&c.fetchRemoteConfig&&(s.remoteConfigClient?(u=s.remoteConfigClient.subscribe("configs.analyticsSDK.browserSDK.customEnrichment","all",function(f){f&&n(f)?r=i(f.body||""):r=i("")}),t=function(){var f;return(f=s.remoteConfigClient)===null||f===void 0?void 0:f.unsubscribe(u)}):e?.debug("Remote config client is not provided, skipping remote config fetch")),[2]})})},execute:function(s){return x(void 0,void 0,void 0,function(){var a;return O(this,function(u){if(r)try{return[2,(a=r(s))!==null&&a!==void 0?a:null]}catch(c){return e?.error("Could not execute custom enrichment function",c),[2,s]}return[2,s]})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(s){return t&&t(),[2]})})}};return o},Da=-1,Mk=(function(e){Dt(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r._diagnosticsSampleRate=0,r._enableRequestBodyCompressionExperimentalValue=!1,r}return t.prototype.init=function(r,n,i){r===void 0&&(r="");var o,s;return arguments.length>2?(o=n,s=i):typeof n=="string"?(o=n,s=void 0):(o=n?.userId,s=n),rt(this._init(D(D({},s),{userId:o,apiKey:r})))},t.prototype._init=function(r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h,v,p,b,E,m,g,y,_,S,A,C,I,P=this;return O(this,function(R){switch(R.label){case 0:return this.initializing?[2]:(this.initializing=!0,f=xC(r),d=(n=r.loggerProvider)!==null&&n!==void 0?n:new Xn,r.loggerProvider||d.enable((i=r.logLevel)!==null&&i!==void 0?i:at.Warn),h=(o=r.serverZone)!==null&&o!==void 0?o:kg,p=this._diagnosticsSampleRate,b=(s=r.enableDiagnostics)!==null&&s!==void 0?s:!0,f?(v=new rg(r.apiKey,d,h,(a=r.remoteConfig)===null||a===void 0?void 0:a.serverUrl),[4,new Promise(function(k){v?.subscribe("configs.diagnostics.browserSDK","all",function(N,$,ee){if(d.debug("Diagnostics remote configuration received:",JSON.stringify({remoteConfig:N,source:$,lastFetch:ee},null,2)),N){var B=N.sampleRate;typeof B=="number"&&!isNaN(B)&&(p=B);var z=N.enabled;typeof z=="boolean"&&(b=z)}k()})})]):[3,2]);case 1:R.sent(),R.label=2;case 2:return E=new iA(r.apiKey,d,h,{enabled:b,sampleRate:p}),E.setTag("library","".concat(Cg,"/").concat(ql)),typeof navigator<"u"&&E.setTag("user_agent",navigator.userAgent),[4,RC(r.apiKey,r,this,E,{loggerProvider:d,serverZone:h,enableDiagnostics:b,diagnosticsSampleRate:p})];case 3:return m=R.sent(),f&&v?[4,new Promise(function(k){v?.subscribe("configs.analyticsSDK.browserSDK","all",function(N,$,ee){m.loggerProvider.debug("Remote configuration received:",JSON.stringify({remoteConfig:N,source:$,lastFetch:ee},null,2)),N&&VC(N,m),k()})})]:[3,5];case 4:R.sent(),R.label=5;case 5:return[4,e.prototype._init.call(this,m)];case 6:return R.sent(),this.logBrowserOptions(m),this.config.remoteConfigClient=v,Eg(this.config.defaultTracking)?(this.config.optOut&&this.timeline.addOptOutListener(function(k){return x(P,void 0,void 0,function(){var N;return O(this,function($){switch($.label){case 0:return k?[3,2]:(N=ad(this.config),this.webAttribution=new Bd(N,this.config),[4,this.webAttribution.init()]);case 1:$.sent(),$.label=2;case 2:return[2]}})})}),g=ad(this.config),this.webAttribution=new Bd(g,this.config),[4,this.webAttribution.init()]):[3,8];case 7:R.sent(),R.label=8;case 8:return y=ys(),_=y.ampTimestamp?Number(y.ampTimestamp):void 0,S=_?Date.now()<_:!0,A=S&&!Number.isNaN(Number(y.ampSessionId))?Number(y.ampSessionId):void 0,C=this.config.deferredSessionId,C===Da&&!this.config.optOut&&(C=Date.now()),this.setSessionId((l=(c=(u=r.sessionId)!==null&&u!==void 0?u:A)!==null&&c!==void 0?c:C)!==null&&l!==void 0?l:this.config.sessionId),this.config.optOut&&this.timeline.addOptOutListener(function(k){return x(P,void 0,void 0,function(){return O(this,function(N){return!k&&this.config.deferredSessionId&&(this.config.deferredSessionId===Da?this.setSessionId(void 0):this.setSessionId(this.config.deferredSessionId)),[2]})})}),I=Yn(r.instanceName),I.identityStore.setIdentity({userId:this.config.userId,deviceId:this.config.deviceId}),this.config.offline===ng?[3,10]:[4,this.add(qC()).promise];case 9:R.sent(),R.label=10;case 10:return[4,this.add(new RT({diagnosticsClient:E})).promise];case 11:return R.sent(),[4,this.add(new oC).promise];case 12:return R.sent(),[4,this.add(new BT).promise];case 13:return R.sent(),jC(this.config),XA(this.config.defaultTracking)?(this.config.loggerProvider.debug("Adding file download tracking plugin"),[4,this.add(BC()).promise]):[3,15];case 14:R.sent(),R.label=15;case 15:return _g(this.config.defaultTracking)?(this.config.loggerProvider.debug("Adding form interaction plugin"),[4,this.add(HC()).promise]):[3,17];case 16:R.sent(),R.label=17;case 17:return Sg(this.config.defaultTracking)?this.config.optOut?[3,19]:(this.config.loggerProvider.debug("Adding page view tracking plugin"),[4,this.add(hd(sd(this.config))).promise]):[3,20];case 18:return R.sent(),[3,20];case 19:this.timeline.addOptOutListener(function(k){return x(P,void 0,void 0,function(){return O(this,function(N){switch(N.label){case 0:return k?[2]:(this.config.loggerProvider.debug("Adding page view tracking plugin"),[4,this.add(hd(sd(this.config))).promise]);case 1:return N.sent(),[2]}})})}),R.label=20;case 20:return Tg(this.config.autocapture)?(this.config.loggerProvider.debug("Adding user interactions plugin (autocapture plugin)"),[4,this.add(JI(ZA(this.config),{diagnosticsClient:E})).promise]):[3,22];case 21:R.sent(),R.label=22;case 22:return Ag(this.config.autocapture)?(this.config.loggerProvider.debug("Adding frustration interactions plugin"),[4,this.add(fk(eC(this.config))).promise]):[3,24];case 23:R.sent(),R.label=24;case 24:return wg(this.config.autocapture)?(this.config.loggerProvider.debug("Adding network tracking plugin"),[4,this.add(yk(tC(this.config))).promise]):[3,26];case 25:R.sent(),R.label=26;case 26:return JA(this.config.autocapture)?(this.config.loggerProvider.debug("Adding web vitals plugin"),[4,this.add(Ok()).promise]):[3,28];case 27:R.sent(),R.label=28;case 28:return YA(this.config.autocapture)?(this.config.loggerProvider.debug("Adding referrer page url plugin"),[4,this.add(Nk()).promise]):[3,30];case 29:R.sent(),R.label=30;case 30:return QA(this.config.customEnrichment)?(this.config.loggerProvider.debug("Adding custom enrichment plugin"),[4,this.add(Lk()).promise]):[3,32];case 31:R.sent(),R.label=32;case 32:return this.initializing=!1,[4,this.runQueuedFunctions("dispatchQ")];case 33:return R.sent(),I.eventBridge.setEventReceiver(function(k){var N=k.eventProperties||{},$=N.time,ee=gs(N,["time"]),B=typeof $=="number"?{time:$}:void 0;P.track(k.eventType,ee,B)}),[2]}})})},t.prototype.getUserId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.userId},t.prototype.setUserId=function(r){if(!this.config){this.q.push(this.setUserId.bind(this,r));return}this.config.loggerProvider.debug("function setUserId: ",r),(r!==this.config.userId||r===void 0)&&(this.config.userId=r,this.timeline.onIdentityChanged({userId:r}),FT(r,this.config.instanceName))},t.prototype.getDeviceId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.deviceId},t.prototype.setDeviceId=function(r){if(!this.config){this.q.push(this.setDeviceId.bind(this,r));return}this.config.loggerProvider.debug("function setDeviceId: ",r),r!==this.config.deviceId&&(this.config.deviceId=r,this.timeline.onIdentityChanged({deviceId:r}),HT(r,this.config.instanceName))},t.prototype.reset=function(){this.setDeviceId(Zt()),this.setUserId(void 0),this.timeline.onReset()},t.prototype.getIdentity=function(){var r,n;return{deviceId:(r=this.config)===null||r===void 0?void 0:r.deviceId,userId:(n=this.config)===null||n===void 0?void 0:n.userId,userProperties:this.userProperties}},t.prototype.setIdentity=function(r){var n,i,o;if("userId"in r&&this.setUserId(r.userId),"deviceId"in r&&r.deviceId&&this.setDeviceId(r.deviceId),"userProperties"in r){this.userProperties=r.userProperties;var s=new Fn,a=(o=r.userProperties)!==null&&o!==void 0?o:{};try{for(var u=ce(Object.entries(a)),c=u.next();!c.done;c=u.next()){var l=oe(c.value,2),f=l[0],d=l[1];s.set(f,d)}}catch(h){n={error:h}}finally{try{c&&!c.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}this.identify(s)}},t.prototype.getOptOut=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.optOut},t.prototype.getSessionId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.sessionId},t.prototype.setSessionId=function(r){var n,i=[];if(!this.config)return this.q.push(this.setSessionId.bind(this,r)),rt(Promise.resolve());if(this.config.optOut)return this.config.deferredSessionId=r??Da,rt(Promise.resolve());if(r===void 0&&(r=Date.now()),r===this.config.sessionId)return rt(Promise.resolve());this.config.loggerProvider.debug("function setSessionId: ",r);var o=this.getSessionId();o!==r&&this.timeline.onSessionIdChanged(r);var s=this.config.lastEventTime,a=(n=this.config.lastEventId)!==null&&n!==void 0?n:-1;this.config.sessionId=r,this.config.lastEventTime=void 0,this.config.pageCounter=0,od(this.config.defaultTracking)&&(o&&s&&i.push(this.track(cd,void 0,{device_id:this.previousSessionDeviceId,event_id:++a,session_id:o,time:s+1,user_id:this.previousSessionUserId}).promise),this.config.lastEventTime=this.config.sessionId);var u=this.trackCampaignEventIfNeeded(++a,i);return this.config.identify&&i.push(this.track(Ll(this.config.identify)).promise),od(this.config.defaultTracking)&&i.push(this.track(ld,void 0,{event_id:u?++a:a,session_id:this.config.sessionId,time:this.config.lastEventTime}).promise),this.previousSessionDeviceId=this.config.deviceId,this.previousSessionUserId=this.config.userId,rt(Promise.all(i))},t.prototype.extendSession=function(){if(!this.config){this.q.push(this.extendSession.bind(this));return}this.config.lastEventTime=Date.now()},t.prototype.setTransport=function(r){if(!this.config){this.q.push(this.setTransport.bind(this,r));return}this.config.transportProvider=Og(r)},t.prototype.identify=function(r,n){if(Ia(r)){var i=r._q;r._q=[],r=Ca(new Fn,i)}return n?.user_id&&this.setUserId(n.user_id),n?.device_id&&this.setDeviceId(n.device_id),e.prototype.identify.call(this,r,n)},t.prototype.groupIdentify=function(r,n,i,o){if(Ia(i)){var s=i._q;i._q=[],i=Ca(new Fn,s)}return e.prototype.groupIdentify.call(this,r,n,i,o)},t.prototype.revenue=function(r,n){if(Ia(r)){var i=r._q;r._q=[],r=Ca(new Gp,i)}return e.prototype.revenue.call(this,r,n)},t.prototype.trackCampaignEventIfNeeded=function(r,n){if(!this.webAttribution||!this.webAttribution.shouldTrackNewCampaign)return!1;var i=this.webAttribution.generateCampaignEvent(r);return n?n.push(this.track(i).promise):this.track(i),this.config.loggerProvider.log("Tracking attribution."),!0},t.prototype.process=function(r){return x(this,void 0,void 0,function(){var n,i,o;return O(this,function(s){return n=Date.now(),i=Xp(this.config.sessionTimeout,this.config.lastEventTime),o=this.webAttribution&&this.webAttribution.shouldSetSessionIdOnNewCampaign(),r.event_type!==ld&&r.event_type!==cd&&(!r.session_id||r.session_id===this.getSessionId())&&(i||o?(this.setSessionId(n),o&&this.config.loggerProvider.log("Created a new session for new campaign.")):i||this.trackCampaignEventIfNeeded()),[2,e.prototype.process.call(this,r)]})})},t.prototype.logBrowserOptions=function(r){try{var n=D(D({},r),{apiKey:r.apiKey.substring(0,10)+"********"});this.config.loggerProvider.debug("Initialized Amplitude with BrowserConfig:",jA(n))}catch(i){this.config.loggerProvider.error("Error logging browser config",i)}},t.prototype._setDiagnosticsSampleRate=function(r){if(!(r>1||r<0)&&!this.config){this._diagnosticsSampleRate=r;return}},t.prototype._enableRequestBodyCompressionExperimental=function(r){if(!this.config){this._enableRequestBodyCompressionExperimentalValue=r;return}},t})(ST),om="[Amplitude]",Dk="".concat(om," Session Replay ID"),Uk=0,Fk=Jn.US,Hk={enabled:!0},zl=1e3,Bk="".concat(om," Session Replay Debug"),jk="amp-block",sm="amp-mask",qk="amp-unmask",am="https://api-sr.amplitude.com/sessions/v2/track",um="https://api-sr.eu.amplitude.com/sessions/v2/track",lm="https://api-sr.stag2.amplitude.com/sessions/v2/track",Vk=1*1e6,$k=3e4,Wk=6e4,Gk=500,Kk=10*1e3,cm=1024,zk=1e3,Pr;(function(e){e.GET_SR_PROPS="get-sr-props",e.DEBUG_INFO="debug-info",e.FETCH_REQUEST="fetch-request",e.METADATA="metadata",e.TARGETING_DECISION="targeting-decision"})(Pr||(Pr={}));var Bu=(function(){function e(t){this.logger=t,this.log=this.getSafeMethod("log"),this.warn=this.getSafeMethod("warn"),this.error=this.getSafeMethod("error"),this.debug=this.getSafeMethod("debug")}return e.prototype.getSafeMethod=function(t){var r;if(!this.logger)return(function(){});var n=this.logger[t];if(typeof n=="function"){var i=(r=n.__rrweb_original__)!==null&&r!==void 0?r:n;return i.bind(this.logger)}return(function(){})},e.prototype.enable=function(t){this.logger.enable(t)},e.prototype.disable=function(){this.logger.disable()},e})(),ju="medium";function Xk(e){return e.toLowerCase()}function Yk(e){var t=e.type;return e.hasAttribute("data-rr-is-password")?"password":t?Xk(t):null}var fm=function(e,t,r){switch(t){case"light":{if(e!=="input")return!0;var n=r?Yk(r):"";return n?!!(["password","hidden","email","tel"].includes(n)||r.autocomplete.startsWith("cc-")):!1}case"medium":case"conservative":return!0;default:return fm(e,ju,r)}},dm=function(e,t,r){var n,i,o;if(t===void 0&&(t={defaultMaskLevel:ju}),r){if(r.closest("."+sm))return!0;var s=((n=t.maskSelector)!==null&&n!==void 0?n:[]).some(function(u){return r.closest(u)});if(s)return!0;if(r.closest("."+qk))return!1;var a=((i=t.unmaskSelector)!==null&&i!==void 0?i:[]).some(function(u){return r.closest(u)});if(a)return!1}return fm(e,(o=t.defaultMaskLevel)!==null&&o!==void 0?o:ju,r)},jd=function(e,t){return function(r,n){return dm(e,t,n)?r.replace(/[^\s]/g,"*"):r}},Jk=function(e){return function(t,r,n){var i;return t==="style"||!((i=e?.maskAttributes)!==null&&i!==void 0?i:[]).includes(t)?r:dm("text",e,n)?r.replace(/[^\s]/g,"*"):r}},Qk=function(){var e=le();return e?.location?e.location.href:""},Zk=function(e,t){return"".concat(t,"/").concat(e)},Xl=function(e,t){return t||(e===Jn.STAGING?lm:e===Jn.EU?um:am)},eP=function(e){if(typeof e!="string"||e.trim()==="")return!1;var t=/^\/|^https?:\/\/[^\s]+$/;return!!t.test(e)},tP=function(e){var t=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp("^".concat(t,"$"))},rP=function(e){if(!e.every(function(t){return typeof t.selector=="string"&&typeof t.replacement=="string"}))throw new Error("ugcFilterRules must be an array of objects with selector and replacement properties");if(!e.every(function(t){return eP(t.selector)}))throw new Error("ugcFilterRules must be an array of objects with valid globs")},Gs=function(e,t){var r,n;try{for(var i=ce(t),o=i.next();!o.done;o=i.next()){var s=o.value,a=tP(s.selector);if(a.test(e))return e.replace(a,s.replacement)}}catch(u){r={error:u}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return e},hm=function(){return x(void 0,void 0,void 0,function(){var e,t,r,n,i,o,s;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,,4]),e=le(),e?[4,e.navigator.storage.estimate()]:[3,2];case 1:return t=a.sent(),r=t.usage,n=t.quota,i=t.usageDetails,o=r?Math.round(r/cm):0,s=r&&n?Math.round((r/n+Number.EPSILON)*1e3)/1e3:0,[2,{totalStorageSize:o,percentOfQuota:s,usageDetails:JSON.stringify(i)}];case 2:return[3,4];case 3:return a.sent(),[3,4];case 4:return[2,{totalStorageSize:0,percentOfQuota:0,usageDetails:""}]}})})},vm=function(e){var t=D({},e),r=t.apiKey;return t.apiKey="****".concat(r.substring(r.length-4)),t},pm=function(){return{flushMaxRetries:2,logLevel:at.Warn,loggerProvider:new Xn,transportProvider:new oA}},nP=(function(e){Dt(t,e);function t(r,n){var i=this,o,s,a,u,c,l,f=pm();if(i=e.call(this,D(D({transportProvider:f.transportProvider,loggerProvider:new Bu(n.loggerProvider||f.loggerProvider)},n),{apiKey:r}))||this,i.flushMaxRetries=n.flushMaxRetries!==void 0&&n.flushMaxRetries<=f.flushMaxRetries?n.flushMaxRetries:f.flushMaxRetries,i.apiKey=r,i.sampleRate=n.sampleRate||Uk,i.serverZone=n.serverZone||Fk,i.configServerUrl=n.configServerUrl,i.trackServerUrl=n.trackServerUrl,i.shouldInlineStylesheet=n.shouldInlineStylesheet,i.version=n.version,i.performanceConfig=n.performanceConfig||Hk,i.storeType=(o=n.storeType)!==null&&o!==void 0?o:"idb",i.applyBackgroundColorToBlockedElements=(s=n.applyBackgroundColorToBlockedElements)!==null&&s!==void 0?s:!1,i.enableUrlChangePolling=(a=n.enableUrlChangePolling)!==null&&a!==void 0?a:!1,i.urlChangePollingInterval=(u=n.urlChangePollingInterval)!==null&&u!==void 0?u:zl,i.captureDocumentTitle=(c=n.captureDocumentTitle)!==null&&c!==void 0?c:!1,n.privacyConfig&&(i.privacyConfig=n.privacyConfig),n.interactionConfig&&(i.interactionConfig=n.interactionConfig,i.interactionConfig.ugcFilterRules&&rP(i.interactionConfig.ugcFilterRules)),n.debugMode&&(i.debugMode=n.debugMode),n.useWebWorker!==void 0)i.useWebWorker=n.useWebWorker;else{var d=n;((l=d.experimental)===null||l===void 0?void 0:l.useWebWorker)!==void 0&&(i.useWebWorker=d.experimental.useWebWorker)}return n.omitElementTags&&(i.omitElementTags=n.omitElementTags),i}return t})(Kp),gm=(e=>(e[e.DomContentLoaded=0]="DomContentLoaded",e[e.Load=1]="Load",e[e.FullSnapshot=2]="FullSnapshot",e[e.IncrementalSnapshot=3]="IncrementalSnapshot",e[e.Meta=4]="Meta",e[e.Custom=5]="Custom",e[e.Plugin=6]="Plugin",e))(gm||{}),mm=(e=>(e[e.MouseUp=0]="MouseUp",e[e.MouseDown=1]="MouseDown",e[e.Click=2]="Click",e[e.ContextMenu=3]="ContextMenu",e[e.DblClick=4]="DblClick",e[e.Focus=5]="Focus",e[e.Blur=6]="Blur",e[e.TouchStart=7]="TouchStart",e[e.TouchMove_Departed=8]="TouchMove_Departed",e[e.TouchEnd=9]="TouchEnd",e[e.TouchCancel=10]="TouchCancel",e))(mm||{}),iP=function(e,t){var r=document.createDocumentFragment(),n=function(i){if(i===void 0&&(i=[]),typeof i=="string"&&(i=[i]),i=i.filter(function(o){try{r.querySelector(o)}catch{return t.warn('[session-replay-browser] omitting selector "'.concat(o,'" because it is invalid')),!1}return!0}),i.length!==0)return i};return e.blockSelector=n(e.blockSelector),e.maskSelector=n(e.maskSelector),e.unmaskSelector=n(e.unmaskSelector),e},oP=(function(){function e(t,r){this.localConfig=r,this.remoteConfigClient=t}return e.prototype.generateJoinedConfig=function(){var t,r,n,i,o;return x(this,void 0,void 0,function(){var s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A=this;return O(this,function(C){switch(C.label){case 0:s=D({},this.localConfig),s.optOut=this.localConfig.optOut,s.captureEnabled=!0,C.label=1;case 1:return C.trys.push([1,3,,4]),[4,new Promise(function(I,P){A.remoteConfigClient.subscribe("configs.sessionReplay","all",function(R,k){var N;if(A.localConfig.loggerProvider.debug("Session Replay remote configuration received from ".concat(k,":"),JSON.stringify(R,null,2)),!R){P(new Error("No remote config received"));return}var $=R,ee=$.sr_sampling_config,B=$.sr_privacy_config,z=$.sr_targeting_config,K=(N=s.interactionConfig)===null||N===void 0?void 0:N.ugcFilterRules;s.interactionConfig=$.sr_interaction_config,s.interactionConfig&&K&&(s.interactionConfig.ugcFilterRules=K),s.loggingConfig=$.sr_logging_config,(ee||B||z)&&(a={},ee&&(a.sr_sampling_config=ee),B&&(a.sr_privacy_config=B),z&&(a.sr_targeting_config=z)),I()})})];case 2:return C.sent(),[3,4];case 3:return u=C.sent(),this.localConfig.loggerProvider.error("Failed to generate joined config: ",u),s.captureEnabled=!1,[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:void 0}];case 4:if(!a)return[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:a}];if(c=a.sr_sampling_config,l=a.sr_privacy_config,f=a.sr_targeting_config,c&&Object.keys(c).length>0?(Object.prototype.hasOwnProperty.call(c,"capture_enabled")?s.captureEnabled=c.capture_enabled:s.captureEnabled=!1,Object.prototype.hasOwnProperty.call(c,"sample_rate")&&(s.sampleRate=c.sample_rate)):(s.captureEnabled=!0,this.localConfig.loggerProvider.debug("Remote config successfully fetched, but no values set for project, Session Replay capture enabled.")),l){d=(t=s.privacyConfig)!==null&&t!==void 0?t:{},h={defaultMaskLevel:(n=(r=l.defaultMaskLevel)!==null&&r!==void 0?r:d.defaultMaskLevel)!==null&&n!==void 0?n:"medium",blockSelector:[],maskSelector:[],unmaskSelector:[],maskAttributes:Ee([],oe(new Set(Ee(Ee([],oe((i=d.maskAttributes)!==null&&i!==void 0?i:[]),!1),oe((o=l.maskAttributes)!==null&&o!==void 0?o:[]),!1))),!1)},v=function(I){var P,R,k,N,$,ee,B,z,K,Q={};typeof I.blockSelector=="string"&&(I.blockSelector=[I.blockSelector]);try{for(var ae=ce((B=I.blockSelector)!==null&&B!==void 0?B:[]),fe=ae.next();!fe.done;fe=ae.next()){var pe=fe.value;Q[pe]="block"}}catch(M){P={error:M}}finally{try{fe&&!fe.done&&(R=ae.return)&&R.call(ae)}finally{if(P)throw P.error}}try{for(var ye=ce((z=I.maskSelector)!==null&&z!==void 0?z:[]),be=ye.next();!be.done;be=ye.next()){var pe=be.value;Q[pe]="mask"}}catch(M){k={error:M}}finally{try{be&&!be.done&&(N=ye.return)&&N.call(ye)}finally{if(k)throw k.error}}try{for(var de=ce((K=I.unmaskSelector)!==null&&K!==void 0?K:[]),me=de.next();!me.done;me=de.next()){var pe=me.value;Q[pe]="unmask"}}catch(M){$={error:M}}finally{try{me&&!me.done&&(ee=de.return)&&ee.call(de)}finally{if($)throw $.error}}return Q},p=D(D({},v(d)),v(l));try{for(b=ce(Object.entries(p)),E=b.next();!E.done;E=b.next())m=oe(E.value,2),g=m[0],y=m[1],y==="mask"?h.maskSelector.push(g):y==="block"?h.blockSelector.push(g):y==="unmask"&&h.unmaskSelector.push(g)}catch(I){_={error:I}}finally{try{E&&!E.done&&(S=b.return)&&S.call(b)}finally{if(_)throw _.error}}s.privacyConfig=iP(h,this.localConfig.loggerProvider)}return f&&Object.keys(f).length>0&&(s.targetingConfig=f),this.localConfig.loggerProvider.debug(JSON.stringify({name:"session replay joined config",config:vm(s)},null,2)),[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:a}]}})})},e})(),sP=function(e,t){return x(void 0,void 0,void 0,function(){var r,n;return O(this,function(i){return r=new nP(e,t),n=new rg(e,r.loggerProvider,r.serverZone,t.configServerUrl),[2,new oP(n,r)]})})},Nt=Uint8Array,gt=Uint16Array,Yi=Uint32Array,Yl=new Nt([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),Jl=new Nt([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),qd=new Nt([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),ym=function(e,t){for(var r=new gt(31),n=0;n<31;++n)r[n]=t+=1<>>1|(Fe&21845)<<1;wr=(wr&52428)>>>2|(wr&13107)<<2,wr=(wr&61680)>>>4|(wr&3855)<<4,Vu[Fe]=((wr&65280)>>>8|(wr&255)<<8)>>>1}var Di=(function(e,t,r){for(var n=e.length,i=0,o=new gt(t);i>>u]=c}else for(a=new gt(n),i=0;i>>15-e[i];return a}),fn=new Nt(288);for(var Fe=0;Fe<144;++Fe)fn[Fe]=8;for(var Fe=144;Fe<256;++Fe)fn[Fe]=9;for(var Fe=256;Fe<280;++Fe)fn[Fe]=7;for(var Fe=280;Fe<288;++Fe)fn[Fe]=8;var ws=new Nt(32);for(var Fe=0;Fe<32;++Fe)ws[Fe]=5;var lP=Di(fn,9,0),cP=Di(ws,5,0),Em=function(e){return(e/8>>0)+(e&7&&1)},_m=function(e,t,r){(r==null||r>e.length)&&(r=e.length);var n=new(e instanceof gt?gt:e instanceof Yi?Yi:Nt)(r-t);return n.set(e.subarray(t,r)),n},sr=function(e,t,r){r<<=t&7;var n=t/8>>0;e[n]|=r,e[n+1]|=r>>>8},pi=function(e,t,r){r<<=t&7;var n=t/8>>0;e[n]|=r,e[n+1]|=r>>>8,e[n+2]|=r>>>16},Ua=function(e,t){for(var r=[],n=0;nd&&(d=o[n].s);var h=new gt(d+1),v=$u(r[l-1],h,0);if(v>t){var n=0,p=0,b=v-t,E=1<t)p+=E-(1<>>=b;p>0;){var g=o[n].s;h[g]=0&&p;--n){var y=o[n].s;h[y]==t&&(--h[y],++p)}v=t}return[new Nt(h),v]},$u=function(e,t,r){return e.s==-1?Math.max($u(e.l,t,r+1),$u(e.r,t,r+1)):t[e.s]=r},$d=function(e){for(var t=e.length;t&&!e[--t];);for(var r=new gt(++t),n=0,i=e[0],o=1,s=function(u){r[n++]=u},a=1;a<=t;++a)if(e[a]==i&&a!=t)++o;else{if(!i&&o>2){for(;o>138;o-=138)s(32754);o>2&&(s(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(s(i),--o;o>6;o-=6)s(8304);o>2&&(s(o-3<<5|8208),o=0)}for(;o--;)s(i);o=1,i=e[a]}return[r.subarray(0,n),t]},gi=function(e,t){for(var r=0,n=0;n>>8,e[i+2]=e[i]^255,e[i+3]=e[i+1]^255;for(var o=0;o4&&!P[qd[k-1]];--k);var N=c+5<<3,$=gi(i,fn)+gi(o,ws)+s,ee=gi(i,d)+gi(o,p)+s+14+3*k+gi(A,P)+(2*A[16]+3*A[17]+7*A[18]);if(N<=$&&N<=ee)return Wu(t,l,e.subarray(u,u+c));var B,z,K,Q;if(sr(t,l,1+(ee<$)),l+=2,ee<$){B=Di(d,h,0),z=d,K=Di(p,b,0),Q=p;var ae=Di(P,R,0);sr(t,l,g-257),sr(t,l+5,S-1),sr(t,l+10,k-4),l+=14;for(var C=0;C15&&(sr(t,l,ye[C]>>>5&127),l+=ye[C]>>>12)}}else B=lP,z=fn,K=cP,Q=ws;for(var C=0;C255){var be=n[C]>>>18&31;pi(t,l,B[be+257]),l+=z[be+257],be>7&&(sr(t,l,n[C]>>>23&31),l+=Yl[be]);var de=n[C]&31;pi(t,l,K[de]),l+=Q[de],de>3&&(pi(t,l,n[C]>>>5&8191),l+=Jl[de])}else pi(t,l,B[n[C]]),l+=z[n[C]];return pi(t,l,B[256]),l+z[256]},fP=new Yi([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),dP=function(e,t,r,n,i,o){var s=e.length,a=new Nt(n+s+5*(1+Math.floor(s/7e3))+i),u=a.subarray(n,a.length-i),c=0;if(!t||s<8)for(var l=0;l<=s;l+=65535){var f=l+65535;f>>13,v=d&8191,p=(1<7e3||P>24576)&&B>423){c=Wd(e,u,0,_,S,A,I,P,k,l-k,c),P=C=I=0,k=l;for(var z=0;z<286;++z)S[z]=0;for(var z=0;z<30;++z)A[z]=0}var K=2,Q=0,ae=v,fe=$-ee&32767;if(B>2&&N==y(l-fe))for(var pe=Math.min(h,B)-1,ye=Math.min(32767,l),be=Math.min(258,B);fe<=ye&&--ae&&$!=ee;){if(e[l+K]==e[l+K-fe]){for(var de=0;deK){if(K=de,Q=fe,de>pe)break;for(var me=Math.min(fe,de-2),M=0,z=0;zM&&(M=re,ee=Z)}}}$=ee,ee=b[$],fe+=$-ee+32768&32767}if(Q){_[P++]=268435456|qu[K]<<18|Vd[Q];var F=qu[K]&31,ge=Vd[Q]&31;I+=Yl[F]+Jl[ge],++S[257+F],++A[ge],R=l+K,++C}else _[P++]=e[l],++S[e[l]]}}c=Wd(e,u,o,_,S,A,I,P,k,l-k,c)}return _m(a,0,n+Em(c)+i)},hP=function(){var e=1,t=0;return{p:function(r){for(var n=e,i=t,o=r.length,s=0;s!=o;){for(var a=Math.min(s+5552,o);s>>8<<16|(t&255)<<8|t>>>8)+((e&255)<<23)*2}}},vP=function(e,t,r,n,i){return dP(e,t.level==null?6:t.level,t.mem==null?Math.ceil(Math.max(8,Math.min(13,Math.log(e.length)))*1.5):12+t.mem,r,n,!0)},pP=function(e,t,r){for(;r;++t)e[t]=r,r>>>=8},gP=function(e,t){var r=t.level,n=r==0?0:r<6?1:r==9?3:2;e[0]=120,e[1]=n<<6|(n?32-2*n:1)};function mP(e,t){t===void 0&&(t={});var r=hP();r.p(e);var n=vP(e,t,2,4);return gP(n,t),pP(n,n.length-4,r.d()),n}function yP(e,t){var r=e.length;if(typeof TextEncoder<"u")return new TextEncoder().encode(e);for(var n=new Nt(e.length+(e.length>>>1)),i=0,o=function(c){n[i++]=c},s=0;sn.length){var a=new Nt(i+8+(r-s<<1));a.set(n),n=a}var u=e.charCodeAt(s);u<128||t?o(u):u<2048?(o(192|u>>>6),o(128|u&63)):u>55295&&u<57344?(u=65536+(u&1047552)|e.charCodeAt(++s)&1023,o(240|u>>>18),o(128|u>>>12&63),o(128|u>>>6&63),o(128|u&63)):(o(224|u>>>12),o(128|u>>>6&63),o(128|u&63))}return _m(n,0,i)}function bP(e,t){for(var r="",n=0;n{const t={...e,v:EP};return bP(mP(yP(JSON.stringify(t))))};var SP=2e3,wP=(function(){function e(t,r,n,i){var o=this,s;this.taskQueue=[],this.isProcessing=!1,this.compressEvent=function(f){var d=_P(f);return JSON.stringify(d)},this.addCompressedEventToManager=function(f,d){o.eventsManager&&o.deviceId&&o.eventsManager.addEvent({event:{type:"replay",data:f},sessionId:d,deviceId:o.deviceId})},this.addCompressedEvent=function(f,d){if(o.worker)try{o.worker.postMessage({event:f,sessionId:d})}catch(v){v.name==="DataCloneError"?o.worker.postMessage(JSON.stringify({event:f,sessionId:d})):o.config.loggerProvider.warn("Unexpected error while posting message to worker:",v)}else{var h=o.compressEvent(f);o.addCompressedEventToManager(h,d)}},this.terminate=function(){var f;(f=o.worker)===null||f===void 0||f.terminate()};var a=le();if(this.canUseIdleCallback=a&&"requestIdleCallback"in a,this.eventsManager=t,this.config=r,this.deviceId=n,this.timeout=((s=r.performanceConfig)===null||s===void 0?void 0:s.timeout)||SP,i){r.loggerProvider.log("Enabling web worker for compression");try{var u=new Blob([i],{type:"application/javascript"}),c=URL.createObjectURL(u),l=new Worker(c);l.onerror=function(f){f.preventDefault(),r.loggerProvider.error("Worker failed, falling back to non-worker compression:",f),l.terminate(),o.worker=void 0},l.onmessage=function(f){var d=f.data,h=d.compressedEvent,v=d.sessionId;o.addCompressedEventToManager(h,v)},this.worker=l}catch(f){r.loggerProvider.error("Failed to create worker, falling back to non-worker compression:",f)}}}return e.prototype.scheduleIdleProcessing=function(){var t=this;this.isProcessing||(this.isProcessing=!0,requestIdleCallback(function(r){t.processQueue(r)},{timeout:this.timeout}))},e.prototype.enqueueEvent=function(t,r){var n;this.canUseIdleCallback&&(!((n=this.config.performanceConfig)===null||n===void 0)&&n.enabled)?(this.config.loggerProvider.debug("Enqueuing event for processing during idle time."),this.taskQueue.push({event:t,sessionId:r}),this.scheduleIdleProcessing()):(this.config.loggerProvider.debug("Processing event without idle callback."),this.addCompressedEvent(t,r))},e.prototype.processQueue=function(t){for(var r=this;this.taskQueue.length>0&&(t.timeRemaining()>0||t.didTimeout);){var n=this.taskQueue.shift();if(n){var i=n.event,o=n.sessionId;this.addCompressedEvent(i,o)}}this.taskQueue.length>0?requestIdleCallback(function(s){r.processQueue(s)},{timeout:this.timeout}):this.isProcessing=!1},e})(),TP="Unexpected error occurred",AP="Network error occurred, event batch rejected",Gd="Session replay event batch rejected due to exceeded retry count",En="Failed to store session replay events in IndexedDB",CP="Session replay event batch not sent due to missing device ID",IP="Session replay event batch not sent due to missing api key",Gu="1.35.1",kP=(function(){function e(t){var r=t.trackServerUrl,n=t.loggerProvider,i=t.payloadBatcher;this.storageKey="",this.retryTimeout=1e3,this.scheduled=null,this.queue=[],this.loggerProvider=n,this.payloadBatcher=i||function(o){return o},this.trackServerUrl=r}return e.prototype.sendEventsList=function(t){this.addToQueue(D(D({},t),{attempts:0,timeout:0}))},e.prototype.addToQueue=function(){for(var t=this,r=[],n=0;n0&&r.schedule(t)})},t))},e.prototype.flush=function(t){return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var r,n,i,o,s,a,u;return O(this,function(c){switch(c.label){case 0:r=this.queue,this.queue=[],this.scheduled&&(clearTimeout(this.scheduled),this.scheduled=null),c.label=1;case 1:c.trys.push([1,6,7,8]),n=ce(r),i=n.next(),c.label=2;case 2:return i.done?[3,5]:(o=i.value,[4,this.send(o,t)]);case 3:c.sent(),c.label=4;case 4:return i=n.next(),[3,2];case 5:return[3,8];case 6:return s=c.sent(),a={error:s},[3,8];case 7:try{i&&!i.done&&(u=n.return)&&u.call(n)}finally{if(a)throw a.error}return[7];case 8:return[2]}})})},e.prototype.send=function(t,r){var n,i;return r===void 0&&(r=!0),x(this,void 0,void 0,function(){var o,s,a,u,c,l,f,d,h,v,p,b,E;return O(this,function(m){switch(m.label){case 0:if(o=t.apiKey,!o)return[2,this.completeRequest({context:t,err:IP})];if(s=t.deviceId,!s)return[2,this.completeRequest({context:t,err:CP})];if(a=Qk(),u=Gu,c=t.sampleRate,l=new URLSearchParams({device_id:s,session_id:"".concat(t.sessionId),type:"".concat(t.type)}),f="".concat(((n=t.version)===null||n===void 0?void 0:n.type)||"standalone","/").concat(((i=t.version)===null||i===void 0?void 0:i.version)||u),d=this.payloadBatcher({version:1,events:t.events}),d.events.length===0)return this.completeRequest({context:t}),[2];m.label=1;case 1:return m.trys.push([1,6,,7]),h={headers:{"Content-Type":"application/json",Accept:"*/*",Authorization:"Bearer ".concat(o),"X-Client-Version":u,"X-Client-Library":f,"X-Client-Url":a.substring(0,zk),"X-Client-Sample-Rate":"".concat(c),"X-Sampling-Hash-Alg":"xxhash32"},body:JSON.stringify(d),method:"POST"},v="".concat(Xl(t.serverZone,this.trackServerUrl),"?").concat(l.toString()),[4,fetch(v,h)];case 2:if(p=m.sent(),p===null)return this.completeRequest({context:t,err:TP}),[2];if(r)return[3,3];b="";try{b=JSON.stringify(p.body,null,2)}catch{}return this.completeRequest({context:t,success:"".concat(p.status,": ").concat(b)}),[3,5];case 3:return[4,this.handleReponse(p.status,t)];case 4:m.sent(),m.label=5;case 5:return[3,7];case 6:return E=m.sent(),this.completeRequest({context:t,err:E}),[3,7];case 7:return[2]}})})},e.prototype.handleReponse=function(t,r){return x(this,void 0,void 0,function(){var n,i;return O(this,function(o){switch(o.label){case 0:switch(n=new no().buildStatus(t),i=n,i){case qe.Success:return[3,1];case qe.Failed:return[3,2]}return[3,4];case 1:return this.handleSuccessResponse(r),[3,5];case 2:return[4,this.handleOtherResponse(r)];case 3:return o.sent(),[3,5];case 4:this.completeRequest({context:r,err:AP}),o.label=5;case 5:return[2]}})})},e.prototype.handleSuccessResponse=function(t){var r=Math.round(new Blob(t.events).size/cm);this.completeRequest({context:t,success:"Session replay event batch tracked successfully for session id ".concat(t.sessionId,", size of events: ").concat(r," KB")})},e.prototype.handleOtherResponse=function(t){return x(this,void 0,void 0,function(){var r;return O(this,function(n){switch(n.label){case 0:return r=t.attempts*this.retryTimeout,t.attempts++,t.attempts>(t.flushMaxRetries||0)?(this.completeRequest({context:t,err:Gd}),[2]):[4,new Promise(function(i){return setTimeout(i,r)})];case 1:return n.sent(),[4,this.send(t,!0)];case 2:return n.sent(),[2]}})})},e.prototype.completeRequest=function(t){var r=t.context,n=t.err,i=t.success;r.onComplete(),n?this.loggerProvider.warn(n):i&&this.loggerProvider.log(i)},e})();const Ku=(e,t)=>t.some(r=>e instanceof r);let Kd,zd;function PP(){return Kd||(Kd=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function RP(){return zd||(zd=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const zu=new WeakMap,Fa=new WeakMap,Ks=new WeakMap;function OP(e){const t=new Promise((r,n)=>{const i=()=>{e.removeEventListener("success",o),e.removeEventListener("error",s)},o=()=>{r(ln(e.result)),i()},s=()=>{n(e.error),i()};e.addEventListener("success",o),e.addEventListener("error",s)});return Ks.set(t,e),t}function xP(e){if(zu.has(e))return;const t=new Promise((r,n)=>{const i=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",s),e.removeEventListener("abort",s)},o=()=>{r(),i()},s=()=>{n(e.error||new DOMException("AbortError","AbortError")),i()};e.addEventListener("complete",o),e.addEventListener("error",s),e.addEventListener("abort",s)});zu.set(e,t)}let Xu={get(e,t,r){if(e instanceof IDBTransaction){if(t==="done")return zu.get(e);if(t==="store")return r.objectStoreNames[1]?void 0:r.objectStore(r.objectStoreNames[0])}return ln(e[t])},set(e,t,r){return e[t]=r,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function Sm(e){Xu=e(Xu)}function NP(e){return RP().includes(e)?function(...t){return e.apply(Yu(this),t),ln(this.request)}:function(...t){return ln(e.apply(Yu(this),t))}}function LP(e){return typeof e=="function"?NP(e):(e instanceof IDBTransaction&&xP(e),Ku(e,PP())?new Proxy(e,Xu):e)}function ln(e){if(e instanceof IDBRequest)return OP(e);if(Fa.has(e))return Fa.get(e);const t=LP(e);return t!==e&&(Fa.set(e,t),Ks.set(t,e)),t}const Yu=e=>Ks.get(e);function wm(e,t,{blocked:r,upgrade:n,blocking:i,terminated:o}={}){const s=indexedDB.open(e,t),a=ln(s);return n&&s.addEventListener("upgradeneeded",u=>{n(ln(s.result),u.oldVersion,u.newVersion,ln(s.transaction),u)}),r&&s.addEventListener("blocked",u=>r(u.oldVersion,u.newVersion,u)),a.then(u=>{o&&u.addEventListener("close",()=>o()),i&&u.addEventListener("versionchange",c=>i(c.oldVersion,c.newVersion,c))}).catch(()=>{}),a}const MP=["get","getKey","getAll","getAllKeys","count"],DP=["put","add","delete","clear"],Ha=new Map;function Xd(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(Ha.get(t))return Ha.get(t);const r=t.replace(/FromIndex$/,""),n=t!==r,i=DP.includes(r);if(!(r in(n?IDBIndex:IDBObjectStore).prototype)||!(i||MP.includes(r)))return;const o=async function(s,...a){const u=this.transaction(s,i?"readwrite":"readonly");let c=u.store;return n&&(c=c.index(a.shift())),(await Promise.all([c[r](...a),i&&u.done]))[0]};return Ha.set(t,o),o}Sm(e=>({...e,get:(t,r,n)=>Xd(t,r)||e.get(t,r,n),has:(t,r)=>!!Xd(t,r)||e.has(t,r)}));const UP=["continue","continuePrimaryKey","advance"],Yd={},Ju=new WeakMap,Tm=new WeakMap,FP={get(e,t){if(!UP.includes(t))return e[t];let r=Yd[t];return r||(r=Yd[t]=function(...n){Ju.set(this,Tm.get(this)[t](...n))}),r}};async function*HP(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const r=new Proxy(t,FP);for(Tm.set(r,t),Ks.set(r,Yu(t));t;)yield r,t=await(Ju.get(r)||t.continue()),Ju.delete(r)}function Jd(e,t){return t===Symbol.asyncIterator&&Ku(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&Ku(e,[IDBIndex,IDBObjectStore])}Sm(e=>({...e,get(t,r,n){return Jd(t,r)?HP:e.get(t,r,n)},has(t,r){return Jd(t,r)||e.has(t,r)}}));var Am=(function(){function e(t){var r=this,n,i,o;this.minInterval=Gk,this.maxInterval=Kk,this.maxPersistedEventsSize=Vk,this.interval=this.minInterval,this._timeAtLastSplit=Date.now(),this.shouldSplitEventsList=function(s,a){var u=r.getStringSize(a),c=r.getEventsArraySize(s);return c+u>=r.maxPersistedEventsSize?!0:Date.now()-r.timeAtLastSplit>r.interval&&s.length?(r.interval=Math.min(r.maxInterval,r.interval+r.minInterval),r._timeAtLastSplit=Date.now(),!0):!1},this.loggerProvider=t.loggerProvider,this.minInterval=(n=t.minInterval)!==null&&n!==void 0?n:this.minInterval,this.maxInterval=(i=t.maxInterval)!==null&&i!==void 0?i:this.maxInterval,this.maxPersistedEventsSize=(o=t.maxPersistedEventsSize)!==null&&o!==void 0?o:this.maxPersistedEventsSize}return Object.defineProperty(e.prototype,"timeAtLastSplit",{get:function(){return this._timeAtLastSplit},enumerable:!1,configurable:!0}),e.prototype.getStringSize=function(t){return t.length},e.prototype.getEventsArraySize=function(t){var r,n,i=0;try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;i+=this.getStringSize(a)}}catch(c){r={error:c}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}var u=2+Math.max(0,t.length-1)+t.length*2;return i+u},e})(),BP=function(e){return typeof e=="object"&&e!==null&&e.name==="AbortError"},lr=function(e,t,r){BP(r)?e.debug(t):e.warn(t)},Ui="sessionCurrentSequence",Fi="sequencesToSend",jP=function(e){var t,r;return e.objectStoreNames.contains(Ui)||(r=e.createObjectStore(Ui,{keyPath:"sessionId"})),e.objectStoreNames.contains(Fi)||(t=e.createObjectStore(Fi,{keyPath:"sequenceId",autoIncrement:!0}),t.createIndex("sessionId","sessionId")),{sequencesStore:t,currentSequenceStore:r}},qP=function(e){return x(void 0,void 0,void 0,function(){return O(this,function(t){switch(t.label){case 0:return[4,wm(e,1,{upgrade:jP})];case 1:return[2,t.sent()]}})})},VP=(function(e){Dt(t,e);function t(r){var n=e.call(this,r)||this;return n.getSequencesToSend=function(){return x(n,void 0,void 0,function(){var i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,5,,6]),i=[],[4,this.db.transaction("sequencesToSend").store.openCursor()];case 1:o=l.sent(),l.label=2;case 2:return o?(s=o.value,a=s.sessionId,u=s.events,i.push({events:u,sequenceId:o.key,sessionId:a}),[4,o.continue()]):[3,4];case 3:return o=l.sent(),[3,2];case 4:return[2,i];case 5:return c=l.sent(),lr(this.loggerProvider,"".concat(En,": ").concat(c),c),[3,6];case 6:return[2,void 0]}})})},n.storeCurrentSequence=function(i){return x(n,void 0,void 0,function(){var o,s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,4,,5]),[4,this.db.get(Ui,i)];case 1:return o=u.sent(),o?[4,this.db.put(Fi,{sessionId:i,events:o.events})]:[2,void 0];case 2:return s=u.sent(),[4,this.db.put(Ui,{sessionId:i,events:[]})];case 3:return u.sent(),[2,D(D({},o),{sessionId:i,sequenceId:s})];case 4:return a=u.sent(),lr(this.loggerProvider,"".concat(En,": ").concat(a),a),[3,5];case 5:return[2,void 0]}})})},n.addEventToCurrentSequence=function(i,o){return x(n,void 0,void 0,function(){var s,a,u,c,l,f;return O(this,function(d){switch(d.label){case 0:return d.trys.push([0,10,,11]),s=this.db.transaction(Ui,"readwrite"),[4,s.store.get(i)];case 1:return a=d.sent(),a?[3,3]:[4,s.store.put({sessionId:i,events:[o]})];case 2:return d.sent(),[2];case 3:return u=void 0,this.shouldSplitEventsList(a.events,o)?(u=a.events,[4,s.store.put({sessionId:i,events:[o]})]):[3,5];case 4:return d.sent(),[3,7];case 5:return c=a.events.concat(o),[4,s.store.put({sessionId:i,events:c})];case 6:d.sent(),d.label=7;case 7:return[4,s.done];case 8:return d.sent(),u?[4,this.storeSendingEvents(i,u)]:[2,void 0];case 9:return l=d.sent(),l?[2,{events:u,sessionId:i,sequenceId:l}]:[2,void 0];case 10:return f=d.sent(),lr(this.loggerProvider,"".concat(En,": ").concat(f),f),[3,11];case 11:return[2,void 0]}})})},n.storeSendingEvents=function(i,o){return x(n,void 0,void 0,function(){var s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,2,,3]),[4,this.db.put(Fi,{sessionId:i,events:o})];case 1:return s=u.sent(),[2,s];case 2:return a=u.sent(),lr(this.loggerProvider,"".concat(En,": ").concat(a),a),[3,3];case 3:return[2,void 0]}})})},n.cleanUpSessionEventsStore=function(i,o){return x(n,void 0,void 0,function(){var s;return O(this,function(a){switch(a.label){case 0:if(!o)return[2];a.label=1;case 1:return a.trys.push([1,3,,4]),[4,this.db.delete(Fi,o)];case 2:return a.sent(),[3,4];case 3:return s=a.sent(),lr(this.loggerProvider,"".concat(En,": ").concat(s),s),[3,4];case 4:return[2]}})})},n.db=r.db,n}return t.new=function(r,n){return x(this,void 0,void 0,function(){var i,o,s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,2,,3]),i=r==="replay"?"":"_".concat(r),o="".concat(n.apiKey.substring(0,10),"_amp_session_replay_events").concat(i),[4,qP(o)];case 1:return s=u.sent(),[2,new t(D(D({},n),{db:s}))];case 2:return a=u.sent(),lr(n.loggerProvider,"".concat(En,": ").concat(a),a),[3,3];case 3:return[2]}})})},t.prototype.getCurrentSequenceEvents=function(r){return x(this,void 0,void 0,function(){var s,n,i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return r?[4,this.db.get("sessionCurrentSequence",r)]:[3,2];case 1:return s=l.sent(),s?[2,[s]]:[2,void 0];case 2:n=[],l.label=3;case 3:return l.trys.push([3,8,9,10]),[4,this.db.getAll("sessionCurrentSequence")];case 4:i=ce.apply(void 0,[l.sent()]),o=i.next(),l.label=5;case 5:if(o.done)return[3,7];s=o.value,n.push(s),l.label=6;case 6:return o=i.next(),[3,5];case 7:return[3,10];case 8:return a=l.sent(),u={error:a},[3,10];case 9:try{o&&!o.done&&(c=i.return)&&c.call(i)}finally{if(u)throw u.error}return[7];case 10:return[2,n]}})})},t})(Am),$P=(function(e){Dt(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.finalizedSequences={},r.sequences={},r.sequenceId=0,r}return t.prototype.resetCurrentSequence=function(r){this.sequences[r]=[]},t.prototype.addSequence=function(r){var n=this.sequenceId++,i=Ee([],oe(this.sequences[r]),!1);return this.finalizedSequences[n]={sessionId:r,events:i},this.resetCurrentSequence(r),{sequenceId:n,events:i,sessionId:r}},t.prototype.getSequencesToSend=function(){return x(this,void 0,void 0,function(){return O(this,function(r){return[2,Object.entries(this.finalizedSequences).map(function(n){var i=oe(n,2),o=i[0],s=i[1],a=s.sessionId,u=s.events;return{sequenceId:Number(o),sessionId:a,events:u}})]})})},t.prototype.storeCurrentSequence=function(r){return x(this,void 0,void 0,function(){return O(this,function(n){return this.sequences[r]?[2,this.addSequence(r)]:[2,void 0]})})},t.prototype.addEventToCurrentSequence=function(r,n){return x(this,void 0,void 0,function(){var i;return O(this,function(o){return this.sequences[r]||this.resetCurrentSequence(r),this.shouldSplitEventsList(this.sequences[r],n)&&(i=this.addSequence(r)),this.sequences[r].push(n),[2,i]})})},t.prototype.storeSendingEvents=function(r,n){return x(this,void 0,void 0,function(){return O(this,function(i){return this.finalizedSequences[this.sequenceId]={sessionId:r,events:n},[2,this.sequenceId++]})})},t.prototype.cleanUpSessionEventsStore=function(r,n){return x(this,void 0,void 0,function(){return O(this,function(i){return n!==void 0&&delete this.finalizedSequences[n],[2]})})},t})(Am),Qd=function(e){var t=e.config,r=e.minInterval,n=e.maxInterval,i=e.type,o=e.payloadBatcher,s=e.storeType;return x(void 0,void 0,void 0,function(){function a(E){return E===void 0&&(E=!1),x(this,void 0,void 0,function(){return O(this,function(m){return[2,u.flush(E)]})})}var u,c,l,f,d,h,v,p,b;return O(this,function(E){switch(E.label){case 0:return u=new kP(D(D({},t),{loggerProvider:t.loggerProvider,payloadBatcher:o})),c=function(){return new $P({loggerProvider:t.loggerProvider,maxInterval:n,minInterval:r})},l=function(){return x(void 0,void 0,void 0,function(){var m;return O(this,function(g){switch(g.label){case 0:return[4,VP.new(i,{loggerProvider:t.loggerProvider,minInterval:r,maxInterval:n,apiKey:t.apiKey})];case 1:return m=g.sent(),m?[2,m]:(t.loggerProvider.log("Failed to initialize idb store, falling back to memory store."),[2,c()])}})})},s!=="idb"?[3,2]:[4,l()];case 1:return d=E.sent(),[3,3];case 2:d=c(),E.label=3;case 3:return f=d,h=function(m){var g=m.events,y=m.sessionId,_=m.deviceId,S=m.sequenceId;t.debugMode&&hm().then(function(A){var C=A.totalStorageSize,I=A.percentOfQuota,P=A.usageDetails;t.loggerProvider.debug("Total storage size: ".concat(C," KB, percentage of quota: ").concat(I,"%, usage details: ").concat(P))}).catch(function(){}),u.sendEventsList({events:g,sessionId:y,flushMaxRetries:t.flushMaxRetries,apiKey:t.apiKey,deviceId:_,sampleRate:t.sampleRate,serverZone:t.serverZone,version:t.version,type:i,onComplete:function(){return x(void 0,void 0,void 0,function(){return O(this,function(A){switch(A.label){case 0:return[4,f.cleanUpSessionEventsStore(y,S)];case 1:return A.sent(),[2]}})})}})},v=function(m){var g=m.sessionId,y=m.deviceId;f.storeCurrentSequence(g).then(function(_){_&&h({sequenceId:_.sequenceId,events:_.events,sessionId:_.sessionId,deviceId:y})}).catch(function(_){t.loggerProvider.warn("Failed to get current sequence of session replay events for session:",_)})},p=function(m){var g=m.deviceId;return x(void 0,void 0,void 0,function(){var y;return O(this,function(_){switch(_.label){case 0:return[4,f.getSequencesToSend()];case 1:return y=_.sent(),y&&y.forEach(function(S){h({sequenceId:S.sequenceId,events:S.events,sessionId:S.sessionId,deviceId:g})}),[2]}})})},b=function(m){var g=m.event,y=m.sessionId,_=m.deviceId;f.addEventToCurrentSequence(y,g.data).then(function(S){return S&&h({sequenceId:S.sequenceId,events:S.events,sessionId:S.sessionId,deviceId:_})}).catch(function(S){t.loggerProvider.warn("Failed to add event to session replay capture:",S)})},[2,{sendCurrentSequenceEvents:v,addEvent:b,sendStoredEvents:p,flush:a}]}})})},Zd=(function(){function e(){for(var t=[],r=0;r0&&(n=i[0]),zs(n)}else throw new Error("Selector was not found.")}function GP(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}function Po(e,t,r){for(var n=null,i=[],o=e,s=0,a=function(){var c,l,f=new Date().getTime()-Cm.getTime();if(Lt.timeoutMs!==void 0&&f>Lt.timeoutMs)throw new Error("Timeout: Can't find a unique selector after ".concat(f,"ms"));var d=Oo(KP(o))||Oo.apply(void 0,Ee([],oe(zP(o)),!1))||Oo.apply(void 0,Ee([],oe(XP(o)),!1))||Oo(YP(o))||[rh()],h=JP(o);if(t=="all")h&&(d=d.concat(d.filter(Ba).map(function(m){return Ro(m,h)})));else if(t=="two")d=d.slice(0,1),h&&(d=d.concat(d.filter(Ba).map(function(m){return Ro(m,h)})));else if(t=="one"){var v=oe(d=d.slice(0,1),1),p=v[0];h&&Ba(p)&&(d=[Ro(p,h)])}else t=="none"&&(d=[rh()],h&&(d=[Ro(d[0],h)]));try{for(var b=(c=void 0,ce(d)),E=b.next();!E.done;E=b.next()){var p=E.value;p.level=s}}catch(m){c={error:m}}finally{try{E&&!E.done&&(l=b.return)&&l.call(b)}finally{if(c)throw c.error}}if(i.push(d),i.length>=Lt.seedMinLength&&(n=eh(i,r),n))return"break";o=o.parentElement,s++};o;){var u=a();if(u==="break")break}return n||(n=eh(i,r)),!n&&r?r():n}function eh(e,t){var r,n,i=Pm(km(e));if(i.length>Lt.threshold)return t?t():null;try{for(var o=ce(i),s=o.next();!s.done;s=o.next()){var a=s.value;if(Im(a))return a}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return null}function zs(e){for(var t=e[0],r=t.name,n=1;n ").concat(r):r="".concat(e[n].name," ").concat(r),t=e[n]}return r}function th(e){return e.map(function(t){return t.penalty}).reduce(function(t,r){return t+r},0)}function Im(e){var t=zs(e);switch(Ql.querySelectorAll(t).length){case 0:throw new Error("Can't select any node with this selector: ".concat(t));case 1:return!0;default:return!1}}function KP(e){var t=e.getAttribute("id");return t&&Lt.idName(t)?{name:"#"+CSS.escape(t),penalty:0}:null}function zP(e){var t=Array.from(e.attributes).filter(function(r){return Lt.attr(r.name,r.value)});return t.map(function(r){return{name:"[".concat(CSS.escape(r.name),'="').concat(CSS.escape(r.value),'"]'),penalty:.5}})}function XP(e){var t=Array.from(e.classList).filter(Lt.className);return t.map(function(r){return{name:"."+CSS.escape(r),penalty:1}})}function YP(e){var t=e.tagName.toLowerCase();return Lt.tagName(t)?{name:t,penalty:2}:null}function rh(){return{name:"*",penalty:3}}function JP(e){var t=e.parentNode;if(!t)return null;var r=t.firstChild;if(!r)return null;for(var n=0;r&&(r.nodeType===Node.ELEMENT_NODE&&n++,r!==e);)r=r.nextSibling;return n}function Ro(e,t){return{name:e.name+":nth-child(".concat(t,")"),penalty:e.penalty+1}}function Ba(e){return e.name!=="html"&&!e.name.startsWith("#")}function Oo(){for(var e=[],t=0;t0?r:null}function QP(e){return e!=null}function km(e,t){var r,n,i,o,s,a;return t===void 0&&(t=[]),O(this,function(u){switch(u.label){case 0:if(!(e.length>0))return[3,9];u.label=1;case 1:u.trys.push([1,6,7,8]),r=ce(e[0]),n=r.next(),u.label=2;case 2:return n.done?[3,5]:(i=n.value,[5,ce(km(e.slice(1,e.length),t.concat(i)))]);case 3:u.sent(),u.label=4;case 4:return n=r.next(),[3,2];case 5:return[3,8];case 6:return o=u.sent(),s={error:o},[3,8];case 7:try{n&&!n.done&&(a=r.return)&&a.call(r)}finally{if(s)throw s.error}return[7];case 8:return[3,11];case 9:return[4,t];case 10:u.sent(),u.label=11;case 11:return[2]}})}function Pm(e){return Ee([],oe(e),!1).sort(function(t,r){return th(t)-th(r)})}function Rm(e,t,r){var n,i,o;return r===void 0&&(r={counter:0,visited:new Map}),O(this,function(s){switch(s.label){case 0:if(!(e.length>2&&e.length>Lt.optimizedMinLength))return[3,5];n=1,s.label=1;case 1:return nLt.maxNumberOfTries?[2]:(r.counter+=1,i=Ee([],oe(e),!1),i.splice(n,1),o=zs(i),r.visited.has(o)?[2]:Im(i)&&ZP(i,t)?[4,i]:[3,4]):[3,5];case 2:return s.sent(),r.visited.set(o,!0),[5,ce(Rm(i,t,r))];case 3:s.sent(),s.label=4;case 4:return n++,[3,1];case 5:return[2]}})}function ZP(e,t){return Ql.querySelector(zs(e))===t}var eR=36e5,tR=function(e){var t=e.version,r=e.events,n=[];return r.forEach(function(i){var o=JSON.parse(i);o.count=1,o.type==="click"&&n.push(o)}),{version:t,events:n}},rR=function(e){var t=e.version,r=e.events,n=[];r.forEach(function(o){var s=JSON.parse(o);s.type==="click"&&n.push(s)});var i=n.reduce(function(o,s){var a=s.x,u=s.y,c=s.selector,l=s.timestamp,f=l-l%eR,d="".concat(a,":").concat(u,":").concat(c??"",":").concat(f);return o[d]?o[d].count+=1:o[d]=D(D({},s),{timestamp:f,count:1}),o},{});return{version:t,events:Object.values(i)}},nR=(function(){function e(t,r){var n=this;this.createHook=function(i){var o=i.eventsManager,s=i.sessionId,a=i.deviceIdFn,u=i.mirror,c=i.ugcFilterRules,l=i.performanceOptions;return function(f){if(f.type===mm.Click){var d=le();if(d){var h=d.location,v=d.innerHeight,p=d.innerWidth;if(h){var b=f.x,E=f.y;if(!(b===void 0||E===void 0)){var m=u.getNode(f.id),g;if(m)try{g=WP(m,l)}catch{n.logger.debug("error resolving selector from finder")}var y=Gs(h.href,c),_={x:b+n.scrollWatcher.currentScrollX,y:E+n.scrollWatcher.currentScrollY,selector:g,viewportHeight:v,viewportWidth:p,pageUrl:y,timestamp:Date.now(),type:"click"},S=a();S&&o.addEvent({sessionId:s,event:{type:"interaction",data:JSON.stringify(_)},deviceId:S})}}}}}},this.logger=t,this.scrollWatcher=r}return e})();function ja(){var e=le();return e?.innerHeight||document.documentElement&&document.documentElement.clientHeight||0}function qa(){var e=le();return e?.innerWidth||document.documentElement&&document.documentElement.clientWidth||0}var iR=(function(){function e(t,r){var n=le();n&&n.navigator&&typeof n.navigator.sendBeacon=="function"?this.sendBeacon=function(i,o){try{if(n.navigator.sendBeacon(i,JSON.stringify(o)))return!0}catch{}return!1}:this.sendBeacon=function(){return!1},this.sendXhr=function(i,o){var s=new XMLHttpRequest;return s.open("POST",i,!0),s.setRequestHeader("Accept","*/*"),s.send(JSON.stringify(o)),!0},this.basePageUrl=Xl(r.serverZone,r.trackServerUrl),this.apiKey=r.apiKey,this.context=t}return e.prototype.send=function(t,r){var n=this.context,i=n.sessionId,o=n.type,s=new URLSearchParams({device_id:t,session_id:String(i),type:String(o),api_key:this.apiKey}),a="".concat(this.basePageUrl,"?").concat(s.toString());this.sendBeacon(a,r)||this.sendXhr(a,r)},e})(),oR=(function(){function e(t,r){var n=this;this.timestamp=Date.now(),this.hook=function(i){n.update(i)},this.send=function(i){return function(o){var s,a,u,c,l=i(),f=le();if(f&&l){var d=(s=f.scrollX)!==null&&s!==void 0?s:0,h=(a=f.scrollY)!==null&&a!==void 0?a:0;(d>0||h>0)&&n.update({id:1,x:d,y:h}),n.transport.send(l,{version:1,events:[{maxScrollX:n._maxScrollX,maxScrollY:n._maxScrollY,maxScrollWidth:n._maxScrollWidth,maxScrollHeight:n._maxScrollHeight,viewportHeight:ja(),viewportWidth:qa(),pageUrl:Gs(f.location.href,(c=(u=n.config.interactionConfig)===null||u===void 0?void 0:u.ugcFilterRules)!==null&&c!==void 0?c:[]),timestamp:n.timestamp,type:"scroll"}]})}}},this._maxScrollX=0,this._maxScrollY=0,this._currentScrollX=0,this._currentScrollY=0,this._maxScrollWidth=qa(),this._maxScrollHeight=ja(),this.config=r,this.transport=t}return e.default=function(t,r){return new e(new iR(t,r),r)},Object.defineProperty(e.prototype,"maxScrollX",{get:function(){return this._maxScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollY",{get:function(){return this._maxScrollY},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollWidth",{get:function(){return this._maxScrollWidth},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollHeight",{get:function(){return this._maxScrollHeight},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentScrollX",{get:function(){return this._currentScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentScrollY",{get:function(){return this._currentScrollY},enumerable:!1,configurable:!0}),e.prototype.update=function(t){var r=Date.now();if(this._currentScrollX=t.x,this._currentScrollY=t.y,t.x>this._maxScrollX){var n=qa();this._maxScrollX=t.x;var i=t.x+n;i>this._maxScrollWidth&&(this._maxScrollWidth=i),this.timestamp=r}if(t.y>this._maxScrollY){var o=ja();this._maxScrollY=t.y;var s=t.y+o;s>this._maxScrollHeight&&(this._maxScrollHeight=s),this.timestamp=r}},e})(),nh=(function(){function e(t){var r=t.sessionId,n=t.deviceId;this.deviceId=n,this.sessionId=r,r&&n&&(this.sessionReplayId=Zk(r,n))}return e})(),sR=1e3*60*60*24*2,aR=(function(){function e(){var t=this;this.dbs={},this.createStore=function(r){return x(t,void 0,void 0,function(){return O(this,function(n){switch(n.label){case 0:return[4,wm(r,1,{upgrade:function(i){i.objectStoreNames.contains("sessionTargetingMatch")||i.createObjectStore("sessionTargetingMatch",{keyPath:"sessionId"})}})];case 1:return[2,n.sent()]}})})},this.openOrCreateDB=function(r){return x(t,void 0,void 0,function(){var n,i;return O(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(n="".concat(r.substring(0,10),"_amp_session_replay_targeting"),[4,this.createStore(n)]);case 1:return i=o.sent(),this.dbs[r]=i,[2,i]}})})},this.getTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId;return x(t,void 0,void 0,function(){var s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return s=l.sent(),a=String(o),[4,s.get("sessionTargetingMatch",a)];case 2:return u=l.sent(),[2,u?.targetingMatch];case 3:return c=l.sent(),lr(n,"Failed to get targeting match for session id ".concat(o,": ").concat(c),c),[3,4];case 4:return[2,void 0]}})})},this.storeTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId,s=r.targetingMatch;return x(t,void 0,void 0,function(){var a,u,c,l;return O(this,function(f){switch(f.label){case 0:return f.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return a=f.sent(),u=String(o),[4,a.put("sessionTargetingMatch",{targetingMatch:s,sessionId:u,lastUpdated:Date.now()})];case 2:return c=f.sent(),[2,c];case 3:return l=f.sent(),lr(n,"Failed to store targeting match for session id ".concat(o,": ").concat(l),l),[3,4];case 4:return[2,void 0]}})})},this.clearStoreOfOldSessions=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.currentSessionId;return x(t,void 0,void 0,function(){var s,a,u,c,l,f,d,h;return O(this,function(v){switch(v.label){case 0:return v.trys.push([0,8,,9]),[4,this.openOrCreateDB(i)];case 1:return s=v.sent(),a=String(o),u=s.transaction("sessionTargetingMatch","readwrite"),[4,u.store.getAll()];case 2:c=v.sent(),l=0,v.label=3;case 3:return lsR?[4,u.store.delete(f.sessionId)]:[3,5]):[3,6];case 4:v.sent(),v.label=5;case 5:return l++,[3,3];case 6:return[4,u.done];case 7:return v.sent(),[3,9];case 8:return h=v.sent(),lr(n,"Failed to clear old targeting matches for sessions: ".concat(h),h),[3,9];case 9:return[2]}})})}}return e})(),Va=new aR,uR=function(e){var t=e.sessionId,r=e.targetingConfig,n=e.loggerProvider,i=e.apiKey,o=e.targetingParams,s=e.urlChange,a=s===void 0?!1:s;return x(void 0,void 0,void 0,function(){var u,c,l,f,d,h,v;return O(this,function(p){switch(p.label){case 0:return[4,Va.clearStoreOfOldSessions({loggerProvider:n,apiKey:i,currentSessionId:t})];case 1:return p.sent(),[4,Va.getTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:t})];case 2:if(u=p.sent(),u===!0&&!a)return[2,!0];c=!0,p.label=3;case 3:return p.trys.push([3,6,,7]),[4,Yt(()=>import("./RzjnwIMW.js"),[],import.meta.url)];case 4:return l=p.sent().evaluateTargeting,f=D(D({},o),{flag:r,sessionId:typeof t=="string"?parseInt(t,10):t,apiKey:i,loggerProvider:n}),[4,l(f)];case 5:return d=p.sent(),d&&d.sr_targeting_config&&(c=d.sr_targeting_config.key==="on"),Va.storeTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:t,targetingMatch:c}),[3,7];case 6:return h=p.sent(),v=h,n.warn(v.message),[3,7];case 7:return[2,c]}})})},Go=2654435761,Ko=2246822519,ih=3266489917,lR=668265263,oh=374761393;function Jr(e,t){return(e<>>32-t)>>>0}function xo(e,t){return e=e+Math.imul(t,Ko)>>>0,e=Jr(e,13),e=Math.imul(e,Go)>>>0,e}function mi(e,t){return(e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24)>>>0}function cR(e){for(var t=[],r=0;r=55296&&n<=56319&&r+1=56320&&i<=57343&&(n=(n-55296<<10)+(i-56320)+65536,r++)}n<128?t.push(n):n<2048?t.push(192|n>>6,128|n&63):n<65536?t.push(224|n>>12,128|n>>6&63,128|n&63):t.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|n&63)}return new Uint8Array(t)}function fR(e,t){t===void 0&&(t=0);var r=cR(e),n=r.length,i,o=0;if(n>=16){for(var s=t+Go+Ko>>>0,a=t+Ko>>>0,u=t>>>0,c=t-Go>>>0;o<=n-16;)s=xo(s,mi(r,o)),o+=4,a=xo(a,mi(r,o)),o+=4,u=xo(u,mi(r,o)),o+=4,c=xo(c,mi(r,o)),o+=4;i=Jr(s,1)+Jr(a,7)+Jr(u,12)+Jr(c,18)>>>0}else i=t+oh>>>0;for(i=i+n>>>0;o<=n-4;)i=i+Math.imul(mi(r,o),ih)>>>0,i=Math.imul(Jr(i,17),lR)>>>0,o+=4;for(;o>>0,i=Math.imul(Jr(i,11),Go)>>>0,o++;return i^=i>>>15,i=Math.imul(i,Ko)>>>0,i^=i>>>13,i=Math.imul(i,ih)>>>0,i^=i>>>16,i>>>0}function dR(e,t){var r=fR(e.toString()),n=r%1e6;return n/1e60?Gs(y,u):y;return{href:S,title:_,viewportHeight:E,viewportWidth:m,type:"url-change-event"}},p=function(){var E=h();if(d===void 0||E!==d){d=E;var m=v();t(m)}},b=Om(r,function(){return p()},c?{enablePolling:!0,pollingInterval:l}:{});return p(),function(){return b()}},options:e}}xm();var hR=(function(){function e(){var t=this;this.name="@amplitude/session-replay-browser",this.recordCancelCallback=null,this.eventCount=0,this.sessionTargetingMatch=!1,this.pageLeaveFns=[],this.recordFunction=null,this.urlChangeCleanup=null,this.latestUrlChangeTargetingEvaluationId=0,this.teardownEventListeners=function(r){var n=le();n&&(n.removeEventListener("blur",t.blurListener),n.removeEventListener("focus",t.focusListener),!r&&n.addEventListener("blur",t.blurListener),!r&&n.addEventListener("focus",t.focusListener),n.self&&"onpagehide"in n.self?(n.removeEventListener("pagehide",t.pageLeaveListener),!r&&n.addEventListener("pagehide",t.pageLeaveListener)):(n.removeEventListener("beforeunload",t.pageLeaveListener),!r&&n.addEventListener("beforeunload",t.pageLeaveListener)))},this.blurListener=function(){t.sendEvents()},this.focusListener=function(){t.recordEvents(!1)},this.pageLeaveListener=function(r){t.pageLeaveFns.forEach(function(n){n(r)})},this.evaluateTargetingAndCapture=function(r,n,i,o){return n===void 0&&(n=!1),i===void 0&&(i=!1),o===void 0&&(o=!1),x(t,void 0,void 0,function(){var s,a,u,c,l,f,d,h,v,p,b,E,m;return O(this,function(g){switch(g.label){case 0:if(!this.identifiers||!this.identifiers.sessionId||!this.config)return this.identifiers&&!this.identifiers.sessionId?this.loggerProvider.log("Session ID has not been set yet, cannot evaluate targeting for Session Replay."):this.loggerProvider.warn("Session replay init has not been called, cannot evaluate targeting."),[2];if(!this.config.targetingConfig)if(n)this.loggerProvider.log("Targeting config has not been set yet, cannot evaluate targeting.");else return this.loggerProvider.log("No targeting config set, skipping initialization/recording for event."),[2];return this.lastTargetingParams=r,s=this.config.targetingConfig,a=s&&!this.sessionTargetingMatch,a?(u=o?this.latestUrlChangeTargetingEvaluationId:void 0,c=r.event,c&&Object.values(St).includes(c.event_type)&&(c=void 0),l=(E=(v=(h=r.page)===null||h===void 0?void 0:h.url)!==null&&v!==void 0?v:(b=(p=le())===null||p===void 0?void 0:p.location)===null||b===void 0?void 0:b.href)!==null&&E!==void 0?E:"",f=(m=r.page)!==null&&m!==void 0?m:l!==""?{url:l}:void 0,[4,uR({sessionId:this.identifiers.sessionId,targetingConfig:s,loggerProvider:this.loggerProvider,apiKey:this.config.apiKey,targetingParams:{userProperties:r.userProperties,event:c,page:f},urlChange:o})]):[3,2];case 1:if(d=g.sent(),o&&u!==void 0&&u!==this.latestUrlChangeTargetingEvaluationId)return this.loggerProvider.debug("Ignoring stale URL-change targeting result #".concat(u,"; latest is #").concat(this.latestUrlChangeTargetingEvaluationId,".")),[2];this.sessionTargetingMatch=this.sessionTargetingMatch||d,this.loggerProvider.debug(JSON.stringify({name:"targeted replay capture config",sessionTargetingMatch:this.sessionTargetingMatch,event:c,targetingParams:r},null,2)),g.label=2;case 2:return n?(this.initialize(!0),[3,5]):[3,3];case 3:return i||!this.recordCancelCallback?(this.loggerProvider.log("Recording events for session due to forceRestart or no ongoing recording."),[4,this.recordEvents()]):[3,5];case 4:g.sent(),g.label=5;case 5:return[2]}})})},this.addCustomRRWebEvent=function(r,n,i){return n===void 0&&(n={}),i===void 0&&(i=!0),x(t,void 0,void 0,function(){var o,s,a,u;return O(this,function(c){switch(c.label){case 0:return c.trys.push([0,3,,4]),o=void 0,s=this.config,s&&r!==Pr.METADATA?(o={config:vm(s),version:Gu},i?[4,hm()]:[3,2]):[3,2];case 1:a=c.sent(),o=D(D({},a),o),c.label=2;case 2:return this.recordCancelCallback&&this.recordFunction?this.recordFunction.addCustomEvent(r,D(D({},n),o)):this.loggerProvider.debug("Not able to add custom replay capture event ".concat(r," due to no ongoing recording.")),[3,4];case 3:return u=c.sent(),this.loggerProvider.debug("Error while adding custom replay capture event: ",u),[3,4];case 4:return[2]}})})},this.stopRecordingEvents=function(){var r;try{t.loggerProvider.log("Session Replay capture stopping."),t.recordCancelCallback&&t.recordCancelCallback(),t.recordCancelCallback=null,(r=t.networkObservers)===null||r===void 0||r.stop()}catch(i){var n=i;t.loggerProvider.warn("Error occurred while stopping replay capture: ".concat(n.toString()))}},this.loggerProvider=new Bu(new Xn)}return e.prototype.init=function(t,r){return rt(this._init(t,r))},e.prototype.setupUrlChangeListenerForTargeting=function(){var t=this,r;(r=this.urlChangeCleanup)===null||r===void 0||r.call(this);var n=le();if(n?.location){var i=function(s){var a=++t.latestUrlChangeTargetingEvaluationId;t.evaluateTargetingAndCapture({userProperties:{},event:void 0,page:{url:s}},!1,!1,!0),t.loggerProvider.debug("Queued URL-change targeting re-evaluation #".concat(a," for ").concat(s,"."))},o=Om(n,i);this.urlChangeCleanup=function(){o(),t.urlChangeCleanup=null}}},e.prototype.getCurrentPageForTargeting=function(){var t,r,n=(r=(t=le())===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.href;return n!=null?{url:n}:void 0},e.prototype._init=function(t,r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h,v,p,b,E,m,g,y,C,_,S,A,C,I,P,R,k;return O(this,function(N){switch(N.label){case 0:return(n=this.urlChangeCleanup)===null||n===void 0||n.call(this),this.loggerProvider=new Bu(r.loggerProvider||new Xn),Object.prototype.hasOwnProperty.call(r,"logLevel")&&this.loggerProvider.enable(r.logLevel),this.identifiers=new nh({sessionId:r.sessionId,deviceId:r.deviceId}),f=this,[4,sP(t,r)];case 1:return f.joinedConfigGenerator=N.sent(),[4,this.joinedConfigGenerator.generateJoinedConfig()];case 2:d=N.sent(),h=d.joinedConfig,v=d.localConfig,p=d.remoteConfig,this.config=h,this.setMetadata(r.sessionId,h,v,p,(i=r.version)===null||i===void 0?void 0:i.version,Gu,(o=r.version)===null||o===void 0?void 0:o.type),r.sessionId&&(!((s=this.config.interactionConfig)===null||s===void 0)&&s.enabled)&&(b=oR.default({sessionId:r.sessionId,type:"interaction"},this.config),this.pageLeaveFns=[b.send(this.getDeviceId.bind(this)).bind(b)],this.scrollHook=b.hook.bind(b),this.clickHandler=new nR(this.loggerProvider,b)),E=[],m=this.config.storeType,m==="idb"&&!(!((a=le())===null||a===void 0)&&a.indexedDB)&&(m="memory",this.loggerProvider.warn("Could not use preferred indexedDB storage, reverting to in memory option.")),this.loggerProvider.log("Using ".concat(m," for event storage.")),N.label=3;case 3:return N.trys.push([3,5,,6]),[4,Qd({config:this.config,type:"replay",storeType:m})];case 4:return g=N.sent(),E.push({name:"replay",manager:g}),[3,6];case 5:return y=N.sent(),C=y,this.loggerProvider.warn("Error occurred while creating replay events manager: ".concat(C.toString())),[3,6];case 6:if(!(!((u=this.config.interactionConfig)===null||u===void 0)&&u.enabled))return[3,10];_=this.config.interactionConfig.batch?rR:tR,N.label=7;case 7:return N.trys.push([7,9,,10]),[4,Qd({config:this.config,type:"interaction",minInterval:(c=this.config.interactionConfig.trackEveryNms)!==null&&c!==void 0?c:$k,maxInterval:Wk,payloadBatcher:_,storeType:m})];case 8:return S=N.sent(),E.push({name:"interaction",manager:S}),[3,10];case 9:return A=N.sent(),C=A,this.loggerProvider.warn("Error occurred while creating interaction events manager: ".concat(C.toString())),[3,10];case 10:return this.eventsManager=new(Zd.bind.apply(Zd,Ee([void 0],oe(E),!1))),this.eventCompressor&&this.eventCompressor.terminate(),I=void 0,P=le(),this.config.useWebWorker&&P&&P.Worker?[4,Yt(()=>import("./D7Bf6Say.js"),[],import.meta.url)]:[3,12];case 11:R=N.sent().compressionScript,I=R,N.label=12;case 12:return this.eventCompressor=new wP(this.eventsManager,this.config,this.getDeviceId(),I),[4,this.initializeNetworkObservers()];case 13:return N.sent(),!((l=le())===null||l===void 0)&&l.opener&&(k=yg(),bg(k),k.setup(D({logger:this.loggerProvider},this.config.serverZone&&{endpoint:gg[this.config.serverZone]}))),this.loggerProvider.log("Installing @amplitude/session-replay-browser."),this.teardownEventListeners(!1),[4,this.evaluateTargetingAndCapture({userProperties:r.userProperties,page:this.getCurrentPageForTargeting()},!0)];case 14:return N.sent(),this.config.targetingConfig&&this.setupUrlChangeListenerForTargeting(),[2]}})})},e.prototype.setSessionId=function(t,r){return rt(this.asyncSetSessionId(t,r))},e.prototype.asyncSetSessionId=function(t,r,n){var i;return x(this,void 0,void 0,function(){var o,s,a;return O(this,function(u){switch(u.label){case 0:return this.latestUrlChangeTargetingEvaluationId++,this.sessionTargetingMatch=!1,this.lastShouldRecordDecision=void 0,o=this.identifiers&&this.identifiers.sessionId,o&&this.sendEvents(o),s=r||this.getDeviceId(),this.identifiers=new nh({sessionId:t,deviceId:s}),this.joinedConfigGenerator&&o?[4,this.joinedConfigGenerator.generateJoinedConfig()]:[3,2];case 1:a=u.sent().joinedConfig,this.config=a,u.label=2;case 2:return!((i=this.config)===null||i===void 0)&&i.targetingConfig?[4,this.evaluateTargetingAndCapture({userProperties:n?.userProperties,page:this.getCurrentPageForTargeting()},!1,!0)]:[3,4];case 3:return u.sent(),[3,6];case 4:return[4,this.recordEvents()];case 5:u.sent(),u.label=6;case 6:return[2]}})})},e.prototype.getSessionReplayProperties=function(){var t,r=this.config,n=this.identifiers;if(!r||!n)return this.loggerProvider.warn("Session replay init has not been called, cannot get session replay properties."),{};var i=this.getShouldRecord(),o={};return i&&(o=(t={},t[Dk]=n.sessionReplayId?n.sessionReplayId:null,t),r.debugMode&&(o[Bk]=JSON.stringify({appHash:Ru(r.apiKey).toString()}))),this.addCustomRRWebEvent(Pr.GET_SR_PROPS,{shouldRecord:i,eventProperties:o},this.eventCount===10),this.eventCount===10&&(this.eventCount=0),this.eventCount++,o},e.prototype.sendEvents=function(t){var r,n=t||((r=this.identifiers)===null||r===void 0?void 0:r.sessionId),i=this.getDeviceId();this.eventsManager&&n&&i&&this.eventsManager.sendCurrentSequenceEvents({sessionId:n,deviceId:i})},e.prototype.initialize=function(t){var r;return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var n;return O(this,function(i){return!((r=this.identifiers)===null||r===void 0)&&r.sessionId?(n=this.getDeviceId(),n?(this.eventsManager&&t&&this.eventsManager.sendStoredEvents({deviceId:n}),[2,this.recordEvents()]):(this.loggerProvider.log("Session is not being recorded due to lack of device id."),[2,Promise.resolve()])):(this.loggerProvider.log("Session is not being recorded due to lack of session id."),[2,Promise.resolve()])})})},e.prototype.shouldOptOut=function(){var t,r,n;if(!((t=this.config)===null||t===void 0)&&t.instanceName){var i=Yn(this.config.instanceName).identityStore;n=i.getIdentity().optOut}return n!==void 0?n:(r=this.config)===null||r===void 0?void 0:r.optOut},e.prototype.getShouldRecord=function(){if(!this.identifiers||!this.config||!this.identifiers.sessionId)return this.loggerProvider.warn("Session is not being recorded due to lack of config, please call sessionReplay.init."),!1;if(!this.config.captureEnabled)return this.loggerProvider.log("Session ".concat(this.identifiers.sessionId," not being captured due to capture being disabled for project or because the remote config could not be fetched.")),!1;if(this.shouldOptOut())return this.loggerProvider.log("Opting session ".concat(this.identifiers.sessionId," out of recording due to optOut config.")),!1;var t=!1,r="",n=!1;if(this.config.targetingConfig)this.sessionTargetingMatch?(r="Capturing replays for session ".concat(this.identifiers.sessionId," due to matching targeting conditions."),this.loggerProvider.log(r),t=!0,n=!0):(r="Not capturing replays for session ".concat(this.identifiers.sessionId," due to not matching targeting conditions."),this.loggerProvider.log(r),t=!1,n=!1);else{var i=dR(this.identifiers.sessionId,this.config.sampleRate);i?(t=!0,n=!0):(r="Opting session ".concat(this.identifiers.sessionId," out of recording due to sample rate."),this.loggerProvider.log(r),t=!1,n=!1)}return this.lastShouldRecordDecision!==t&&this.config.targetingConfig&&(this.addCustomRRWebEvent(Pr.TARGETING_DECISION,{message:r,sessionId:this.identifiers.sessionId,matched:n,targetingParams:this.lastTargetingParams}),this.lastShouldRecordDecision=t),t},e.prototype.getBlockSelectors=function(){var t,r,n,i=(n=(r=(t=this.config)===null||t===void 0?void 0:t.privacyConfig)===null||r===void 0?void 0:r.blockSelector)!==null&&n!==void 0?n:[];if(i.length!==0)return i},e.prototype.getMaskTextSelectors=function(){var t,r,n,i;if(((r=(t=this.config)===null||t===void 0?void 0:t.privacyConfig)===null||r===void 0?void 0:r.defaultMaskLevel)==="conservative")return"*";var o=(i=(n=this.config)===null||n===void 0?void 0:n.privacyConfig)===null||i===void 0?void 0:i.maskSelector;if(o)return o},e.prototype.getRecordingPlugins=function(t){var r,n,i,o,s,a;return x(this,void 0,void 0,function(){var u,c,l,f;return O(this,function(d){switch(d.label){case 0:u=[];try{c=xm({ugcFilterRules:((n=(r=this.config)===null||r===void 0?void 0:r.interactionConfig)===null||n===void 0?void 0:n.ugcFilterRules)||[],enablePolling:((i=this.config)===null||i===void 0?void 0:i.enableUrlChangePolling)||!1,pollingInterval:(o=this.config)===null||o===void 0?void 0:o.urlChangePollingInterval,captureDocumentTitle:(s=this.config)===null||s===void 0?void 0:s.captureDocumentTitle}),u.push(c)}catch(h){this.loggerProvider.warn("Failed to create URL tracking plugin:",h)}if(!(!((a=t?.console)===null||a===void 0)&&a.enabled))return[3,4];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,Yt(()=>import("./sPdhR0i8.js"),[],import.meta.url)];case 2:return l=d.sent().getRecordConsolePlugin,u.push(l({level:t.console.levels})),[3,4];case 3:return f=d.sent(),this.loggerProvider.warn("Failed to load console plugin:",f),[3,4];case 4:return[2,u.length>0?u:void 0]}})})},e.prototype.getRecordFunction=function(){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:if(this.recordFunction)return[2,this.recordFunction];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,Yt(()=>import("./DMrp4Fbu.js"),[],import.meta.url)];case 2:return t=n.sent().record,this.recordFunction=t,[2,t];case 3:return r=n.sent(),this.loggerProvider.warn("Failed to load rrweb-record module:",r),[2,null];case 4:return[2]}})})},e.prototype.recordEvents=function(t){var r,n,i,o,s,a,u,c;return t===void 0&&(t=!0),x(this,void 0,void 0,function(){var l,f,d,h,v,p,b,E,m,g,y,_,S,A,C,I,P=this;return O(this,function(R){switch(R.label){case 0:return l=this.config,f=this.getShouldRecord(),d=(r=this.identifiers)===null||r===void 0?void 0:r.sessionId,!f||!d||!l?[2]:(this.stopRecordingEvents(),[4,this.getRecordFunction()]);case 1:return h=R.sent(),h?[4,this.initializeNetworkObservers()]:[2];case 2:R.sent(),v=(n=l.loggingConfig)===null||n===void 0?void 0:n.network,p=Xl(l.serverZone,l.trackServerUrl),b=[am,um,lm,p],(i=this.networkObservers)===null||i===void 0||i.start(function(k){b.some(function(N){return k.url.startsWith(N)})||P.addCustomRRWebEvent(Pr.FETCH_REQUEST,k)},v),E=l.privacyConfig,m=l.interactionConfig,g=l.loggingConfig,y=m?.enabled?{mouseInteraction:this.eventsManager&&((o=this.clickHandler)===null||o===void 0?void 0:o.createHook({eventsManager:this.eventsManager,sessionId:d,deviceIdFn:this.getDeviceId.bind(this),mirror:h.mirror,ugcFilterRules:(s=m.ugcFilterRules)!==null&&s!==void 0?s:[],performanceOptions:(a=l.performanceConfig)===null||a===void 0?void 0:a.interaction})),scroll:this.scrollHook}:{},_=m?.enabled&&m.ugcFilterRules?m.ugcFilterRules:[],this.loggerProvider.log("Session Replay capture beginning for ".concat(d,".")),R.label=3;case 3:return R.trys.push([3,5,,6]),S=this,A=h,I={emit:function(k){if(P.shouldOptOut()){P.loggerProvider.log("Opting session ".concat(d," out of recording due to optOut config.")),P.stopRecordingEvents(),P.sendEvents();return}k.type===gm.Meta&&(k.data.href=Gs(k.data.href,_)),P.eventCompressor&&P.eventCompressor.enqueueEvent(k,d)},inlineStylesheet:l.shouldInlineStylesheet,hooks:y,maskAllInputs:!0,maskTextClass:sm,blockClass:jk,blockSelector:this.getBlockSelectors(),applyBackgroundColorToBlockedElements:l.applyBackgroundColorToBlockedElements,maskInputFn:jd("input",E),maskTextFn:jd("text",E),maskAttributeFn:Jk(E),maskTextSelector:this.getMaskTextSelectors(),recordCanvas:!1,slimDOMOptions:{script:(u=l.omitElementTags)===null||u===void 0?void 0:u.script,comment:(c=l.omitElementTags)===null||c===void 0?void 0:c.comment},errorHandler:function(k){var N=k;if(N.message.includes("insertRule")&&N.message.includes("CSSStyleSheet")||N._external_)throw N;return P.loggerProvider.warn("Error while capturing replay: ",N.toString()),!0}},[4,this.getRecordingPlugins(g)];case 4:return S.recordCancelCallback=A.apply(void 0,[(I.plugins=R.sent(),I)]),this.addCustomRRWebEvent(Pr.DEBUG_INFO),t&&this.addCustomRRWebEvent(Pr.METADATA,this.metadata),[3,6];case 5:return C=R.sent(),this.loggerProvider.warn("Failed to initialize session replay:",C),[3,6];case 6:return[2]}})})},e.prototype.getDeviceId=function(){var t;return(t=this.identifiers)===null||t===void 0?void 0:t.deviceId},e.prototype.getSessionId=function(){var t;return(t=this.identifiers)===null||t===void 0?void 0:t.sessionId},e.prototype.flush=function(t){var r;return t===void 0&&(t=!1),x(this,void 0,void 0,function(){return O(this,function(n){return[2,(r=this.eventsManager)===null||r===void 0?void 0:r.flush(t)]})})},e.prototype.shutdown=function(){var t;(t=this.urlChangeCleanup)===null||t===void 0||t.call(this),this.teardownEventListeners(!0),this.stopRecordingEvents(),this.sendEvents()},e.prototype.mapSDKType=function(t){return t==="plugin"?"@amplitude/plugin-session-replay-browser":t==="segment"?"@amplitude/segment-session-replay-plugin":null},e.prototype.setMetadata=function(t,r,n,i,o,s,a){var u=t?.toString()?Ru(t.toString()):void 0;this.metadata={joinedConfig:r,localConfig:n,remoteConfig:i,sessionId:t,hashValue:u,sampleRate:r.sampleRate,replaySDKType:this.mapSDKType(a),replaySDKVersion:o,standaloneSDKType:"@amplitude/session-replay-browser",standaloneSDKVersion:s}},e.prototype.initializeNetworkObservers=function(){var t,r,n;return x(this,void 0,void 0,function(){var i,o;return O(this,function(s){switch(s.label){case 0:if(!(!((n=(r=(t=this.config)===null||t===void 0?void 0:t.loggingConfig)===null||r===void 0?void 0:r.network)===null||n===void 0)&&n.enabled&&!this.networkObservers))return[3,4];s.label=1;case 1:return s.trys.push([1,3,,4]),[4,Yt(()=>import("./DfTiELZi.js"),[],import.meta.url)];case 2:return i=s.sent().NetworkObservers,this.networkObservers=new i,[3,4];case 3:return o=s.sent(),this.loggerProvider.warn("Failed to import or instantiate NetworkObservers:",o),[3,4];case 4:return[2]}})})},e})(),zr=function(e){return function(){var t=e.config,r=t||pm(),n=r.loggerProvider,i=r.logLevel;return{logger:n,logLevel:i}}},vR=function(){var e=new hR;return{init:_e(e.init.bind(e),"init",zr(e)),evaluateTargetingAndCapture:_e(e.evaluateTargetingAndCapture.bind(e),"evaluateTargetingAndRecord",zr(e)),setSessionId:_e(e.setSessionId.bind(e),"setSessionId",zr(e)),getSessionId:_e(e.getSessionId.bind(e),"getSessionId",zr(e)),getSessionReplayProperties:_e(e.getSessionReplayProperties.bind(e),"getSessionReplayProperties",zr(e)),flush:_e(e.flush.bind(e),"flush",zr(e)),shutdown:_e(e.shutdown.bind(e),"shutdown",zr(e))}};const vn=vR();var pR=vn.init,gR=vn.setSessionId,mR=vn.getSessionId,yR=vn.getSessionReplayProperties,bR=vn.flush,ER=vn.shutdown,_R=vn.evaluateTargetingAndCapture,SR=function(e){return e===void 0&&(e="$default_instance"),Ml.getInstance(e)},Rr;(function(e){e.SET="$set",e.SET_ONCE="$setOnce",e.ADD="$add",e.APPEND="$append",e.PREPEND="$prepend",e.REMOVE="$remove",e.PREINSERT="$preInsert",e.POSTINSERT="$postInsert",e.UNSET="$unset",e.CLEAR_ALL="$clearAll"})(Rr||(Rr={}));var ah;(function(e){e.REVENUE_PRODUCT_ID="$productId",e.REVENUE_QUANTITY="$quantity",e.REVENUE_PRICE="$price",e.REVENUE_TYPE="$revenueType",e.REVENUE_CURRENCY="$currency",e.REVENUE="$revenue"})(ah||(ah={}));var uh;(function(e){e.IDENTIFY="$identify",e.GROUP_IDENTIFY="$groupidentify",e.REVENUE="revenue_amount"})(uh||(uh={}));var wR=[Rr.SET,Rr.SET_ONCE,Rr.ADD,Rr.APPEND,Rr.PREPEND,Rr.POSTINSERT],TR=function(e){if(e.user_properties){var t={},r=Object.keys(e.user_properties);return r.forEach(function(n){if(wR.includes(n)){var i=e.user_properties&&e.user_properties[n];t=D(D({},t),i)}}),t}},lh="1.27.6",AR="[Amplitude]",CR="".concat(AR," Session Replay ID"),Qu=(function(){function e(t){this.name=e.pluginName,this.type="enrichment",this.config=null,this.sessionReplay={flush:bR,getSessionId:mR,getSessionReplayProperties:yR,init:pR,setSessionId:gR,shutdown:ER,evaluateTargetingAndCapture:_R},this.options=D({forceSessionTracking:!1},t),this.srInitOptions=this.options}return e.prototype.setup=function(t,r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h;return O(this,function(v){switch(v.label){case 0:return v.trys.push([0,2,,3]),t?.loggerProvider.log("Installing @amplitude/plugin-session-replay, version ".concat(lh,".")),this.config=t,this.options.forceSessionTracking&&(typeof t.defaultTracking=="boolean"?t.defaultTracking===!1&&(t.defaultTracking={pageViews:!1,formInteractions:!1,fileDownloads:!1,sessions:!0}):t.defaultTracking=D(D({},t.defaultTracking),{sessions:!0})),f=SR(this.config.instanceName).identityStore,d=f.getIdentity().userProperties,this.srInitOptions={instanceName:this.config.instanceName,deviceId:(n=this.options.deviceId)!==null&&n!==void 0?n:this.config.deviceId,optOut:this.config.optOut,sessionId:this.options.customSessionId?void 0:this.config.sessionId,loggerProvider:this.config.loggerProvider,logLevel:this.config.logLevel,flushMaxRetries:this.config.flushMaxRetries,serverZone:this.config.serverZone,configServerUrl:this.options.configServerUrl||((i=this.config.remoteConfig)===null||i===void 0?void 0:i.serverUrl),trackServerUrl:this.options.trackServerUrl,sampleRate:this.options.sampleRate,privacyConfig:{blockSelector:(o=this.options.privacyConfig)===null||o===void 0?void 0:o.blockSelector,maskSelector:(s=this.options.privacyConfig)===null||s===void 0?void 0:s.maskSelector,unmaskSelector:(a=this.options.privacyConfig)===null||a===void 0?void 0:a.unmaskSelector,defaultMaskLevel:(u=this.options.privacyConfig)===null||u===void 0?void 0:u.defaultMaskLevel},debugMode:this.options.debugMode,shouldInlineStylesheet:this.options.shouldInlineStylesheet,version:{type:"plugin",version:lh},performanceConfig:this.options.performanceConfig,storeType:this.options.storeType,useWebWorker:(c=this.options.useWebWorker)!==null&&c!==void 0?c:(l=this.options.experimental)===null||l===void 0?void 0:l.useWebWorker,userProperties:d,omitElementTags:this.options.omitElementTags,applyBackgroundColorToBlockedElements:this.options.applyBackgroundColorToBlockedElements,interactionConfig:this.options.interactionConfig,captureDocumentTitle:this.options.captureDocumentTitle,enableUrlChangePolling:this.options.enableUrlChangePolling,urlChangePollingInterval:this.options.urlChangePollingInterval},[4,this.sessionReplay.init(t.apiKey,this.srInitOptions).promise];case 1:return v.sent(),[3,3];case 2:return h=v.sent(),t?.loggerProvider.error("Session Replay: Failed to initialize due to ".concat(h.message)),[3,3];case 3:return[2]}})})},e.prototype.onSessionIdChanged=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){switch(n.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("Analytics session id is changed to ".concat(t,", SR session id is ").concat(String(this.sessionReplay.getSessionId()),".")),[4,this.sessionReplay.setSessionId(t).promise];case 1:return n.sent(),[2]}})})},e.prototype.onOptOutChanged=function(t){var r;return x(this,void 0,void 0,function(){var n;return O(this,function(i){switch(i.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("optOut is changed to ".concat(String(t),", calling ").concat(t?"sessionReplay.shutdown()":"sessionReplay.init()",".")),t?(this.sessionReplay.shutdown(),[3,4]):[3,1];case 1:return n=this.config!=null,n?[4,this.sessionReplay.init(this.config.apiKey,this.srInitOptions).promise]:[3,3];case 2:n=i.sent(),i.label=3;case 3:i.label=4;case 4:return[2]}})})},e.prototype.execute=function(t){var r,n,i;return x(this,void 0,void 0,function(){var o,u,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,9,,10]),this.options.customSessionId?(o=this.options.customSessionId(t),o?o===this.sessionReplay.getSessionId()?[3,2]:[4,this.sessionReplay.setSessionId(o).promise]:[3,3]):[3,4];case 1:l.sent(),l.label=2;case 2:u=this.sessionReplay.getSessionReplayProperties(),t.event_properties=D(D({},t.event_properties),u),l.label=3;case 3:return[3,8];case 4:return o=(r=this.config)===null||r===void 0?void 0:r.sessionId,o&&o!==this.sessionReplay.getSessionId()?[4,this.sessionReplay.setSessionId(o).promise]:[3,6];case 5:l.sent(),l.label=6;case 6:return o&&o===t.session_id?(s=void 0,t.event_type===St.IDENTIFY&&(s=TR(t)),a=le(),[4,this.sessionReplay.evaluateTargetingAndCapture({event:t,userProperties:s,page:((n=a?.location)===null||n===void 0?void 0:n.href)!=null?{url:a.location.href}:void 0})]):[3,8];case 7:l.sent(),u=this.sessionReplay.getSessionReplayProperties(),t.event_properties=D(D({},t.event_properties),u),l.label=8;case 8:return t.event_type===St.IDENTIFY&&t.event_properties&&delete t.event_properties[CR],[2,Promise.resolve(t)];case 9:return c=l.sent(),(i=this.config)===null||i===void 0||i.loggerProvider.error("Session Replay: Failed to enrich event due to ".concat(c.message)),[2,Promise.resolve(t)];case 10:return[2]}})})},e.prototype.teardown=function(){var t;return x(this,void 0,void 0,function(){return O(this,function(r){try{this.sessionReplay.shutdown(),this.config=null}catch(n){(t=this.config)===null||t===void 0||t.loggerProvider.error("Session Replay: teardown failed due to ".concat(n.message))}return[2]})})},e.prototype.getSessionReplayProperties=function(){return this.sessionReplay.getSessionReplayProperties()},e.pluginName="@amplitude/plugin-session-replay-browser",e})(),IR=function(e){return new Qu(e)},Zu=function(e,t){return Zu=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Zu(e,t)};function Nm(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Zu(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var Nr=function(){return Nr=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Zl(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function ec(e,t,r){if(arguments.length===2)for(var n=0,i=t.length,o;n>6|192,t[r++]=i&63|128):(i&64512)==55296&&n+1>18|240,t[r++]=i>>12&63|128,t[r++]=i>>6&63|128,t[r++]=i&63|128):(t[r++]=i>>12|224,t[r++]=i>>6&63|128,t[r++]=i&63|128)}return Uint8Array.from(t)},zo=-862048943,Xo=461845907,Yo=15,PR=13,RR=5,OR=-430675100,xR=function(e,t){t===void 0&&(t=0);for(var r=kR(e),n=r.length,i=n>>2,o=t,s=0;s>>0},NR=function(e,t){var r=e,n=t;return r=Math.imul(r,zo),r=Hi(r,Yo),r=Math.imul(r,Xo),n^=r,n=Hi(n,PR),n=Math.imul(n,RR),n+OR|0},LR=function(e){var t=e;return t^=t>>>16,t=Math.imul(t,-2048144789),t^=t>>>13,t=Math.imul(t,-1028477387),t^=t>>>16,t},Hi=function(e,t,r){r===void 0&&(r=32),t>r&&(t=t%r);var n=4294967295<>>0,i=(e&n)>>>0>>>r-t>>>0;return(e<>>0},MR=function(e,t){t===void 0&&(t=0);var r=e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3];return DR(r)},DR=function(e){return(e&-16777216)>>>24|(e&16711680)>>>8|(e&65280)<<8|(e&255)<<24},ch=function(e,t){var r,n;if(!(!t||t.length===0)){try{for(var i=kt(t),o=i.next();!o.done;o=i.next()){var s=o.value;if(!s||!e||typeof e!="object")return;e=e[s]}}catch(a){r={error:a}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}if(e!=null)return e}},UR="(\\d+)\\.(\\d+)",FR="(\\d+)",HR="(-(([-\\w]+\\.?)*))?",BR="^".concat(UR,"(\\.").concat(FR).concat(HR,")?$"),jR=(function(){function e(t,r,n,i){i===void 0&&(i=void 0),this.major=t,this.minor=r,this.patch=n,this.preRelease=i}return e.parse=function(t){if(t){var r=new RegExp(BR).exec(t);if(r){var n=Number(r[1]),i=Number(r[2]);if(!(isNaN(n)||isNaN(i))){var o=Number(r[4])||0,s=r[5]||void 0;return new e(n,i,o,s)}}}},e.prototype.compareTo=function(t){return this.major>t.major?1:this.majort.minor?1:this.minort.patch?1:this.patcht.preRelease?1:this.preRelease=p&&l=y&&f<_)return g.variant}}catch(S){o={error:S}}finally{try{m&&!m.done&&(s=E.return)&&s.call(E)}finally{if(o)throw o.error}}}}catch(S){n={error:S}}finally{try{h&&!h.done&&(i=d.return)&&i.call(d)}finally{if(n)throw n.error}}return r.variant},e.prototype.matchNull=function(t,r){var n=this.containsNone(r);switch(t){case se.IS:case se.CONTAINS:case se.LESS_THAN:case se.LESS_THAN_EQUALS:case se.GREATER_THAN:case se.GREATER_THAN_EQUALS:case se.VERSION_LESS_THAN:case se.VERSION_LESS_THAN_EQUALS:case se.VERSION_GREATER_THAN:case se.VERSION_GREATER_THAN_EQUALS:case se.SET_IS:case se.SET_CONTAINS:case se.SET_CONTAINS_ANY:return n;case se.IS_NOT:case se.DOES_NOT_CONTAIN:case se.SET_DOES_NOT_CONTAIN:case se.SET_DOES_NOT_CONTAIN_ANY:return!n;default:return!1}},e.prototype.matchSet=function(t,r,n){switch(r){case se.SET_IS:return this.setEquals(t,n);case se.SET_IS_NOT:return!this.setEquals(t,n);case se.SET_CONTAINS:return this.matchesSetContainsAll(t,n);case se.SET_DOES_NOT_CONTAIN:return!this.matchesSetContainsAll(t,n);case se.SET_CONTAINS_ANY:return this.matchesSetContainsAny(t,n);case se.SET_DOES_NOT_CONTAIN_ANY:return!this.matchesSetContainsAny(t,n);default:return!1}},e.prototype.matchString=function(t,r,n){var i=this;switch(r){case se.IS:return this.matchesIs(t,n);case se.IS_NOT:return!this.matchesIs(t,n);case se.CONTAINS:return this.matchesContains(t,n);case se.DOES_NOT_CONTAIN:return!this.matchesContains(t,n);case se.LESS_THAN:case se.LESS_THAN_EQUALS:case se.GREATER_THAN:case se.GREATER_THAN_EQUALS:return this.matchesComparable(t,r,n,function(o){return i.parseNumber(o)},this.comparator);case se.VERSION_LESS_THAN:case se.VERSION_LESS_THAN_EQUALS:case se.VERSION_GREATER_THAN:case se.VERSION_GREATER_THAN_EQUALS:return this.matchesComparable(t,r,n,function(o){return jR.parse(o)},this.versionComparator);case se.REGEX_MATCH:return this.matchesRegex(t,n);case se.REGEX_DOES_NOT_MATCH:return!this.matchesRegex(t,n);default:return!1}},e.prototype.matchesIs=function(t,r){if(this.containsBooleans(r)){var n=t.toLowerCase();if(n==="true"||n==="false")return r.some(function(i){return i.toLowerCase()===n})}return r.some(function(i){return t===i})},e.prototype.matchesContains=function(t,r){var n,i;try{for(var o=kt(r),s=o.next();!s.done;s=o.next()){var a=s.value;if(t.toLowerCase().includes(a.toLowerCase()))return!0}}catch(u){n={error:u}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}return!1},e.prototype.matchesComparable=function(t,r,n,i,o){var s=this,a=i(t),u=n.map(function(c){return i(c)}).filter(function(c){return c!==void 0});return a===void 0||u.length===0?n.some(function(c){return s.comparator(t,r,c)}):u.some(function(c){return o(a,r,c)})},e.prototype.comparator=function(t,r,n){switch(r){case se.LESS_THAN:case se.VERSION_LESS_THAN:return tn;case se.GREATER_THAN_EQUALS:case se.VERSION_GREATER_THAN_EQUALS:return t>=n;default:return!1}},e.prototype.versionComparator=function(t,r,n){var i=t.compareTo(n);switch(r){case se.LESS_THAN:case se.VERSION_LESS_THAN:return i<0;case se.LESS_THAN_EQUALS:case se.VERSION_LESS_THAN_EQUALS:return i<=0;case se.GREATER_THAN:case se.VERSION_GREATER_THAN:return i>0;case se.GREATER_THAN_EQUALS:case se.VERSION_GREATER_THAN_EQUALS:return i>=0;default:return!1}},e.prototype.matchesRegex=function(t,r){return r.some(function(n){return!!new RegExp(n).exec(t)})},e.prototype.containsNone=function(t){return t.some(function(r){return r==="(none)"})},e.prototype.containsBooleans=function(t){return t.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},e.prototype.parseNumber=function(t){var r;return(r=Number(t))!==null&&r!==void 0?r:void 0},e.prototype.coerceString=function(t){if(t!=null)return typeof t=="object"?JSON.stringify(t):String(t)},e.prototype.coerceStringArray=function(t){var r=this;if(Array.isArray(t)){var n=t;return n.map(function(a){return r.coerceString(a)}).filter(Boolean)}var i=String(t);try{var o=JSON.parse(i);if(Array.isArray(o)){var n=t;return n.map(function(u){return r.coerceString(u)}).filter(Boolean)}else{var s=this.coerceString(i);return s?[s]:void 0}}catch{var s=this.coerceString(i);return s?[s]:void 0}},e.prototype.isSetOperator=function(t){switch(t){case se.SET_IS:case se.SET_IS_NOT:case se.SET_CONTAINS:case se.SET_DOES_NOT_CONTAIN:case se.SET_CONTAINS_ANY:case se.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},e.prototype.setEquals=function(t,r){var n=new Set(t),i=new Set(r);return n.size===i.size&&ec([],Zl(i),!1).every(function(o){return n.has(o)})},e.prototype.matchesSetContainsAll=function(t,r){var n,i;if(t.length{let t={};return e.forEach((r,n)=>t[r]=n),t})(_i),GR=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,nt=String.fromCharCode.bind(String),hh=typeof Uint8Array.from=="function"?Uint8Array.from.bind(Uint8Array):e=>new Uint8Array(Array.prototype.slice.call(e,0)),Fm=e=>e.replace(/=/g,"").replace(/[+\/]/g,t=>t=="+"?"-":"_"),Hm=e=>e.replace(/[^A-Za-z0-9\+\/]/g,""),Bm=e=>{let t,r,n,i,o="";const s=e.length%3;for(let a=0;a255||(n=e.charCodeAt(a++))>255||(i=e.charCodeAt(a++))>255)throw new TypeError("invalid character found");t=r<<16|n<<8|i,o+=_i[t>>18&63]+_i[t>>12&63]+_i[t>>6&63]+_i[t&63]}return s?o.slice(0,s-3)+"===".substring(s):o},tc=typeof btoa=="function"?e=>btoa(e):ni?e=>Buffer.from(e,"binary").toString("base64"):Bm,el=ni?e=>Buffer.from(e).toString("base64"):e=>{let r=[];for(let n=0,i=e.length;nt?Fm(el(e)):el(e),KR=e=>{if(e.length<2){var t=e.charCodeAt(0);return t<128?e:t<2048?nt(192|t>>>6)+nt(128|t&63):nt(224|t>>>12&15)+nt(128|t>>>6&63)+nt(128|t&63)}else{var t=65536+(e.charCodeAt(0)-55296)*1024+(e.charCodeAt(1)-56320);return nt(240|t>>>18&7)+nt(128|t>>>12&63)+nt(128|t>>>6&63)+nt(128|t&63)}},zR=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,jm=e=>e.replace(zR,KR),vh=ni?e=>Buffer.from(e,"utf8").toString("base64"):dh?e=>el(dh.encode(e)):e=>tc(jm(e)),jn=(e,t=!1)=>t?Fm(vh(e)):vh(e),ph=e=>jn(e,!0),XR=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,YR=e=>{switch(e.length){case 4:var t=(7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3),r=t-65536;return nt((r>>>10)+55296)+nt((r&1023)+56320);case 3:return nt((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return nt((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},qm=e=>e.replace(XR,YR),Vm=e=>{if(e=e.replace(/\s+/g,""),!GR.test(e))throw new TypeError("malformed base64.");e+="==".slice(2-(e.length&3));let t,r="",n,i;for(let o=0;o>16&255):i===64?nt(t>>16&255,t>>8&255):nt(t>>16&255,t>>8&255,t&255);return r},rc=typeof atob=="function"?e=>atob(Hm(e)):ni?e=>Buffer.from(e,"base64").toString("binary"):Vm,$m=ni?e=>hh(Buffer.from(e,"base64")):e=>hh(rc(e).split("").map(t=>t.charCodeAt(0))),Wm=e=>$m(Gm(e)),JR=ni?e=>Buffer.from(e,"base64").toString("utf8"):fh?e=>fh.decode($m(e)):e=>qm(rc(e)),Gm=e=>Hm(e.replace(/[-_]/g,t=>t=="-"?"+":"/")),tl=e=>JR(Gm(e)),QR=e=>{if(typeof e!="string")return!1;const t=e.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(t)||!/[^\s0-9a-zA-Z\-_]/.test(t)},Km=e=>({value:e,enumerable:!1,writable:!0,configurable:!0}),zm=function(){const e=(t,r)=>Object.defineProperty(String.prototype,t,Km(r));e("fromBase64",function(){return tl(this)}),e("toBase64",function(t){return jn(this,t)}),e("toBase64URI",function(){return jn(this,!0)}),e("toBase64URL",function(){return jn(this,!0)}),e("toUint8Array",function(){return Wm(this)})},Xm=function(){const e=(t,r)=>Object.defineProperty(Uint8Array.prototype,t,Km(r));e("toBase64",function(t){return Jo(this,t)}),e("toBase64URI",function(){return Jo(this,!0)}),e("toBase64URL",function(){return Jo(this,!0)})},ZR=()=>{zm(),Xm()},rl={version:Um,VERSION:$R,atob:rc,atobPolyfill:Vm,btoa:tc,btoaPolyfill:Bm,fromBase64:tl,toBase64:jn,encode:jn,encodeURI:ph,encodeURL:ph,utob:jm,btou:qm,decode:tl,isValid:QR,fromUint8Array:Jo,toUint8Array:Wm,extendString:zm,extendUint8Array:Xm,extendBuiltins:ZR};var Ym=(function(e){Nm(t,e);function t(r,n){var i=e.call(this,n)||this;return i.statusCode=r,Object.setPrototypeOf(i,t.prototype),i}return t})(Error),nl=(function(e){Nm(t,e);function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return t})(Error),eO=(function(){function e(t,r,n){this.deploymentKey=t,this.serverUrl=r,this.httpClient=n}return e.prototype.getVariants=function(t,r){return Lm(this,void 0,void 0,function(){var n,i,o,s;return Mm(this,function(a){switch(a.label){case 0:return n=rl.encodeURL(JSON.stringify(t)),i={Authorization:"Api-Key ".concat(this.deploymentKey),"X-Amp-Exp-User":n},r?.flagKeys&&(i["X-Amp-Exp-Flag-Keys"]=rl.encodeURL(JSON.stringify(r.flagKeys))),r?.trackingOption&&(i["X-Amp-Exp-Track"]=r.trackingOption),r?.exposureTrackingOption&&(i["X-Amp-Exp-Exposure-Track"]=r.exposureTrackingOption),o=new URL("".concat(this.serverUrl,"/sdk/v2/vardata?v=0")),r?.evaluationMode&&o.searchParams.append("eval_mode",r?.evaluationMode),r?.deliveryMethod&&o.searchParams.append("delivery_method",r?.deliveryMethod),[4,this.httpClient.request({requestUrl:o.toString(),method:"GET",headers:i,timeoutMillis:r?.timeoutMillis})];case 1:if(s=a.sent(),s.status!=200)throw new Ym(s.status,"Fetch error response: status=".concat(s.status));return[2,JSON.parse(s.body)]}})})},e})(),tO=(function(){function e(t,r,n){this.deploymentKey=t,this.serverUrl=r,this.httpClient=n}return e.prototype.getFlags=function(t){return Lm(this,void 0,void 0,function(){var r,n,i;return Mm(this,function(o){switch(o.label){case 0:return r={Authorization:"Api-Key ".concat(this.deploymentKey)},t?.libraryName&&t?.libraryVersion&&(r["X-Amp-Exp-Library"]="".concat(t.libraryName,"/").concat(t.libraryVersion)),t?.user&&(r["X-Amp-Exp-User"]=rl.encodeURL(JSON.stringify(t.user))),[4,this.httpClient.request({requestUrl:"".concat(this.serverUrl,"/sdk/v2/flags")+(t?.deliveryMethod?"?delivery_method=".concat(t.deliveryMethod):""),method:"GET",headers:r,timeoutMillis:t?.timeoutMillis})];case 1:if(n=o.sent(),n.status!=200)throw Error("Flags error response: status=".concat(n.status));return i=JSON.parse(n.body),[2,i.reduce(function(s,a){return s[a.key]=a,s},{})]}})})},e})(),Ge=typeof globalThis<"u"?globalThis:global||self,pn=function(){if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},rO=function(){var e=pn();if(e)try{var t="EXP_test";return e.localStorage.setItem(t,t),e.localStorage.removeItem(t),!0}catch{return!1}return!1},nO=(function(){function e(t,r){this.poller=void 0,this.action=t,this.ms=r}return e.prototype.start=function(){this.poller||(this.poller=Ge.setInterval(this.action,this.ms),this.action())},e.prototype.stop=function(){this.poller&&(Ge.clearInterval(this.poller),this.poller=void 0)},e})(),Si={exports:{}},iO=Si.exports,gh;function oO(){return gh||(gh=1,(function(e,t){(function(r,n){var i="0.7.33",o="",s="?",a="function",u="undefined",c="object",l="string",f="major",d="model",h="name",v="type",p="vendor",b="version",E="architecture",m="console",g="mobile",y="tablet",_="smarttv",S="wearable",A="embedded",C=350,I="Amazon",P="Apple",R="ASUS",k="BlackBerry",N="Browser",$="Chrome",ee="Edge",B="Firefox",z="Google",K="Huawei",Q="LG",ae="Microsoft",fe="Motorola",pe="Opera",ye="Samsung",be="Sharp",de="Sony",me="Xiaomi",M="Zebra",Z="Facebook",X=function(W,j){var J={};for(var ne in W)j[ne]&&j[ne].length%2===0?J[ne]=j[ne].concat(W[ne]):J[ne]=W[ne];return J},re=function(W){for(var j={},J=0;J0?ue.length===2?typeof ue[1]==a?this[ue[0]]=ue[1].call(this,Oe):this[ue[0]]=ue[1]:ue.length===3?typeof ue[1]===a&&!(ue[1].exec&&ue[1].test)?this[ue[0]]=Oe?ue[1].call(this,Oe,ue[2]):n:this[ue[0]]=Oe?Oe.replace(ue[1],ue[2]):n:ue.length===4&&(this[ue[0]]=Oe?ue[3].call(this,Oe.replace(ue[1],ue[2])):n):this[ue]=Oe||n;J+=2}},H=function(W,j){for(var J in j)if(typeof j[J]===c&&j[J].length>0){for(var ne=0;neC?T(ie,C):ie,this},this.setUA(J),this};G.VERSION=i,G.BROWSER=re([h,b,f]),G.CPU=re([E]),G.DEVICE=re([d,p,v,m,g,_,y,S,A]),G.ENGINE=G.OS=re([h,b]),e.exports&&(t=e.exports=G),t.UAParser=G;var Y=typeof r!==u&&(r.jQuery||r.Zepto);if(Y&&!Y.ua){var V=new G;Y.ua=V.getResult(),Y.ua.get=function(){return V.getUA()},Y.ua.set=function(W){V.setUA(W);var j=V.getResult();for(var J in j)Y.ua[J]=j[J]}}})(typeof window=="object"?window:iO)})(Si,Si.exports)),Si.exports}var sO=oO(),Te=function(){return Te=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function ct(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function Rt(e,t,r){if(arguments.length===2)for(var n=0,i=t.length,o;n=2&&v[1]&&(p=atob(v[1])),{deviceId:v[0],userId:p}}catch{return}},aO=function(e){var t=nc(e,!0);try{var r=Ge.localStorage.getItem(t);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},uO=function(e){var t=nc(e,!0);try{var r=Ge.sessionStorage.getItem(t);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},nc=function(e,t){if(t)return e?.length<10?void 0:"AMP_".concat(e.substring(0,10));if(!(e?.length<6))return"amp_".concat(e.substring(0,6))},lO=(function(){function e(t,r,n){this.type="integration",this.apiKey=t,this.identityStore=r.identityStore,this.eventBridge=r.eventBridge,this.contextProvider=r.applicationContextProvider,this.timeoutMillis=n,this.loadPersistedState(),n<=0&&(this.setup=void 0)}return e.prototype.setup=function(t,r){return Ze(this,void 0,void 0,function(){return et(this,function(n){return t?.automaticFetchOnAmplitudeIdentityChange&&this.identityStore.addIdentityListener(function(){r?.fetch()}),[2,this.waitForConnectorIdentity(this.timeoutMillis)]})})},e.prototype.getUser=function(){var t=this.identityStore.getIdentity();return{user_id:t.userId,device_id:t.deviceId,user_properties:t.userProperties,version:this.contextProvider.versionName}},e.prototype.track=function(t){return this.eventBridge.receiver?(this.eventBridge.logEvent({eventType:t.eventType,eventProperties:t.eventProperties}),!0):!1},e.prototype.loadPersistedState=function(){if(!this.apiKey||this.apiKey.startsWith("client-"))return!1;var t=mh(this.apiKey,!0);return t?(this.commitIdentityToConnector(t),!0):(t=mh(this.apiKey,!1),t?(this.commitIdentityToConnector(t),!0):(t=aO(this.apiKey),t?(this.commitIdentityToConnector(t),!0):(t=uO(this.apiKey),t?(this.commitIdentityToConnector(t),!0):!1)))},e.prototype.commitIdentityToConnector=function(t){var r=this.identityStore.editIdentity();r.setDeviceId(t.deviceId),t.userId&&r.setUserId(t.userId),r.commit()},e.prototype.waitForConnectorIdentity=function(t){return Ze(this,void 0,void 0,function(){var r,n=this;return et(this,function(i){return r=this.identityStore.getIdentity(),!r.userId&&!r.deviceId?[2,Promise.race([new Promise(function(o){var s=function(){o(),n.identityStore.removeIdentityListener(s)};n.identityStore.addIdentityListener(s)}),new Promise(function(o,s){Ge.setTimeout(s,t,"Timed out waiting for Amplitude Analytics SDK to initialize.")})])]:[2]})})},e})();function cO(e,t){return t=t||{},new Promise(function(r,n){var i=new XMLHttpRequest,o=[],s=[],a={},u=function(){return{ok:(i.status/100|0)==2,statusText:i.statusText,status:i.status,url:i.responseURL,text:function(){return Promise.resolve(i.responseText)},json:function(){return Promise.resolve(JSON.parse(i.responseText))},blob:function(){return Promise.resolve(new Blob([i.response]))},clone:u,headers:{keys:function(){return o},entries:function(){return s},get:function(l){return a[l.toLowerCase()]},has:function(l){return l.toLowerCase()in a}}}};for(var c in i.open(t.method||"get",e,!0),i.onload=function(){i.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,function(l,f,d){o.push(f=f.toLowerCase()),s.push([f,d]),a[f]=a[f]?a[f]+","+d:d}),r(u())},i.onerror=n,i.withCredentials=t.credentials=="include",t.headers)i.setRequestHeader(c,t.headers[c]);i.send(t.body||null)})}var fO=Ge.fetch||cO,dO=function(e,t){return t==null||t<=0?e:new Promise(function(r,n){Ge.setTimeout(function(){n(new nl("Request timeout after "+t+" milliseconds"))},t),e.then(r,n)})},hO=function(e,t,r,n,i){var o=function(){return Ze(void 0,void 0,void 0,function(){var s,a,u;return et(this,function(c){switch(c.label){case 0:return[4,fO(e,{method:t,headers:r,body:n})];case 1:return s=c.sent(),u={status:s.status},[4,s.text()];case 2:return a=(u.body=c.sent(),u),[2,a]}})})};return dO(o(),i)},vO=(function(){function e(t){this.client=t}return e.prototype.request=function(t){return Ze(this,void 0,void 0,function(){return et(this,function(r){switch(r.label){case 0:return[4,this.client.request(t.requestUrl,t.method,t.headers,null,t.timeoutMillis)];case 1:return[2,r.sent()]}})})},e})(),Jm={request:hO},Ht;(function(e){e[e.Disable=0]="Disable",e[e.Error=1]="Error",e[e.Warn=2]="Warn",e[e.Info=3]="Info",e[e.Debug=4]="Debug",e[e.Verbose=5]="Verbose"})(Ht||(Ht={}));var cr;(function(e){e.LocalStorage="localStorage",e.InitialVariants="initialVariants"})(cr||(cr={}));var Xe;(function(e){e.LocalStorage="storage",e.InitialVariants="initial",e.SecondaryLocalStorage="secondary-storage",e.SecondaryInitialVariants="secondary-initial",e.FallbackInline="fallback-inline",e.FallbackConfig="fallback-config",e.LocalEvaluation="local-evaluation"})(Xe||(Xe={}));var yh=function(e){return!e||e===Xe.FallbackInline||e===Xe.FallbackConfig||e===Xe.SecondaryInitialVariants},Pn={debug:!1,logLevel:Ht.Error,loggerProvider:null,instanceName:"$default_instance",fallbackVariant:{},initialVariants:{},initialFlags:void 0,source:cr.LocalStorage,serverUrl:"https://api.lab.amplitude.com",flagsServerUrl:"https://flag.lab.amplitude.com",serverZone:"US",fetchTimeoutMillis:1e4,retryFetchOnFailure:!0,throwOnError:!1,automaticExposureTracking:!0,pollOnStart:!0,flagConfigPollingIntervalMillis:3e5,fetchOnStart:!0,automaticFetchOnAmplitudeIdentityChange:!1,userProvider:null,analyticsProvider:null,exposureTrackingProvider:null,httpClient:Jm},bh="1.20.4",pO=512,gO=(function(){function e(t,r){var n=this,i;this.isReady=new Promise(function(s){n.resolve=s}),this.config=t,this.client=r;var o=(i=t.instanceName)!==null&&i!==void 0?i:Pn.instanceName;this.queue=new yO(o),this.cache=new mO(o)}return e.prototype.ready=function(){return this.integration?this.isReady:Promise.resolve()},e.prototype.setIntegration=function(t){var r=this;this.integration&&this.integration.teardown&&this.integration.teardown(),this.integration=t,t.setup?this.integration.setup(this.config,this.client).then(function(){r.queue.setTracker(r.integration.track.bind(t)),r.resolve()},function(){r.queue.setTracker(r.integration.track.bind(t)),r.resolve()}):(this.queue.setTracker(this.integration.track.bind(t)),this.resolve())},e.prototype.getUser=function(){return this.integration?this.integration.getUser():{}},e.prototype.track=function(t,r){if(this.cache.shouldTrack(t,r)){var n=this.getExposureEvent(t);this.queue.push(n)}},e.prototype.getExposureEvent=function(t){var r,n,i,o={eventType:"$exposure",eventProperties:t};return!((r=t.metadata)===null||r===void 0)&&r.exposureEvent?o={eventType:(n=t.metadata)===null||n===void 0?void 0:n.exposureEvent,eventProperties:t}:((i=t.metadata)===null||i===void 0?void 0:i.deliveryMethod)==="web"&&(o={eventType:"$impression",eventProperties:t}),o},e})(),mO=(function(){function e(t){this.isSessionStorageAvailable=bO(),this.inMemoryCache={},this.identity={},this.storageKey="EXP_sent_v3_".concat(t),this.isSessionStorageAvailable&&(Ge.sessionStorage.removeItem("EXP_sent_".concat(t)),Ge.sessionStorage.removeItem("EXP_sent_v2_".concat(t)))}return e.prototype.shouldTrack=function(t,r){var n,i;if(((n=t.metadata)===null||n===void 0?void 0:n.deliveryMethod)==="web")return!0;var o={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,o)||this.clearCache(),this.identity=o,this.loadCache();var s=t.flag_key in this.inMemoryCache,a=this.inMemoryCache[t.flag_key],u=(i=t.variant)!==null&&i!==void 0?i:null,c=a??null,l=!1;return(!s||c!==u)&&(l=!0,this.inMemoryCache[t.flag_key]=u),this.storeCache(),l},e.prototype.clearCache=function(){this.inMemoryCache={},this.isSessionStorageAvailable&&Ge.sessionStorage.removeItem(this.storageKey)},e.prototype.identityEquals=function(t,r){return t.userId&&r.userId?t.userId===r.userId:!t.userId&&!r.userId?t.deviceId===r.deviceId:!1},e.prototype.loadCache=function(){if(this.isSessionStorageAvailable){var t=Ge.sessionStorage.getItem(this.storageKey);this.inMemoryCache=t?JSON.parse(t):{}}},e.prototype.storeCache=function(){if(this.isSessionStorageAvailable)try{Ge.sessionStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryCache))}catch{}},e})(),yO=(function(){function e(t,r){r===void 0&&(r=pO),this.isLocalStorageAvailable=rO(),this.inMemoryQueue=[],this.storageKey="EXP_unsent_".concat(t),this.maxQueueSize=r}return e.prototype.push=function(t){this.loadQueue(),this.inMemoryQueue.push(t),this.flush(),this.storeQueue()},e.prototype.setTracker=function(t){var r=this;this.tracker=t,this.poller=Ge.setInterval(function(){r.loadFlushStore()},1e3),this.loadFlushStore()},e.prototype.flush=function(){if(this.tracker&&this.inMemoryQueue.length!==0){for(var t=0;tthis.maxQueueSize&&(this.inMemoryQueue=this.inMemoryQueue.slice(this.inMemoryQueue.length-this.maxQueueSize)),Ge.localStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryQueue)))},e.prototype.loadFlushStore=function(){this.loadQueue(),this.flush(),this.storeQueue()},e})(),bO=function(){var e=pn();if(e)try{var t="EXP_test";return e.sessionStorage.setItem(t,t),e.sessionStorage.removeItem(t),!0}catch{return!1}return!1},EO=(function(){function e(t,r){r===void 0&&(r=Ht.Error),this.logger=t,this.logLevel=r}return e.prototype.error=function(t){for(var r,n=[],i=1;i=Ht.Error&&(r=this.logger).error.apply(r,Rt([t],ct(n),!1))},e.prototype.warn=function(t){for(var r,n=[],i=1;i=Ht.Warn&&(r=this.logger).warn.apply(r,Rt([t],ct(n),!1))},e.prototype.info=function(t){for(var r,n=[],i=1;i=Ht.Info&&(r=this.logger).info.apply(r,Rt([t],ct(n),!1))},e.prototype.debug=function(t){for(var r,n=[],i=1;i=Ht.Debug&&(r=this.logger).debug.apply(r,Rt([t],ct(n),!1))},e.prototype.verbose=function(t){for(var r,n=[],i=1;i=Ht.Verbose&&(r=this.logger).verbose.apply(r,Rt([t],ct(n),!1))},e})(),_O=(function(){function e(){}return e.prototype.error=function(t){for(var r=[],n=1;n0&&f[0]){var d=f[0],h={group_name:d},v=(i=(n=e.group_properties)===null||n===void 0?void 0:n[l])===null||i===void 0?void 0:i[d];v&&Object.keys(v).length>0&&(h.group_properties=v),a[l]=h}}}catch(p){t={error:p}}finally{try{c&&!c.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}return Object.keys(a).length>0&&(o.groups=a),delete o.user.groups,delete o.user.group_properties,o},ft=function(e){return e==null?{}:typeof e=="string"?{key:e,value:e}:e},_h=function(e){if(!e)return{};var t=void 0;e.metadata&&(t=e.metadata.experimentKey);var r={};return e.key&&(r.key=e.key),e.value&&(r.value=e.value),e.payload&&(r.payload=e.payload),t&&(r.expKey=t),e.metadata&&(r.metadata=e.metadata),r},RO=(function(){function e(t){this.setProperties={},this.unsetProperties={},this.analyticsProvider=t}return e.prototype.track=function(t){this.setProperties[t.key]!=t.variant.value&&(this.setProperties[t.key]=t.variant.value,delete this.unsetProperties[t.key],this.analyticsProvider.track(t))},e.prototype.setUserProperty=function(t){this.setProperties[t.key]!=t.variant.value&&this.analyticsProvider.setUserProperty(t)},e.prototype.unsetUserProperty=function(t){this.unsetProperties[t.key]||(this.unsetProperties[t.key]="unset",delete this.setProperties[t.key],this.analyticsProvider.unsetUserProperty(t))},e})(),OO=(function(){function e(t){this.tracked={},this.identity={},this.exposureTrackingProvider=t}return e.prototype.track=function(t,r){var n={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,n)||(this.tracked={}),this.identity=n;var i=t.flag_key in this.tracked,o=this.tracked[t.flag_key];i&&o===t.variant||(this.tracked[t.flag_key]=t.variant,this.exposureTrackingProvider.track(t))},e.prototype.identityEquals=function(t,r){return t.userId===r.userId&&t.deviceId===r.deviceId},e})(),xO=1e4,NO=8,LO=500,MO=1e4,DO=1.5,Sh=6e4,UO="https://api.lab.eu.amplitude.com",FO="https://flag.lab.eu.amplitude.com",HO=(function(){function e(t,r){var n=this,i,o,s,a;this.engine=new qR,this.isRunning=!1,this.apiKey=t,r=IO(r),this.config=Te(Te(Te({},Pn),r),{serverUrl:r?.serverUrl||(((i=r?.serverZone)===null||i===void 0?void 0:i.toLowerCase())==="eu"?UO:Pn.serverUrl),flagsServerUrl:r?.flagsServerUrl||(((o=r?.serverZone)===null||o===void 0?void 0:o.toLowerCase())==="eu"?FO:Pn.flagsServerUrl),flagConfigPollingIntervalMillis:r.flagConfigPollingIntervalMillis=500||t.statusCode===429:!0},e.prototype.addPlugin=function(t){t.type==="integration"&&this.integrationManager.setIntegration(t)},e})(),BO=(function(){function e(t,r){var n,i,o;this.globalScope=pn(),this.userAgent=typeof((n=this.globalScope)===null||n===void 0?void 0:n.navigator)<"u"?(i=this.globalScope)===null||i===void 0?void 0:i.navigator.userAgent:void 0,this.ua=new sO.UAParser(this.userAgent).getResult(),this.localStorage=new Xs,this.sessionStorage=new ey,this.userProvider=t,this.apiKey=r,this.storageKey="EXP_".concat((o=this.apiKey)===null||o===void 0?void 0:o.slice(0,10),"_DEFAULT_USER_PROVIDER")}return e.prototype.getUser=function(){var t,r,n,i,o,s=((t=this.userProvider)===null||t===void 0?void 0:t.getUser())||{};return Te({language:this.getLanguage(),platform:"Web",os:this.getOs(this.ua),device_model:this.getDeviceModel(this.ua),device_category:(n=(r=this.ua.device)===null||r===void 0?void 0:r.type)!==null&&n!==void 0?n:"desktop",referring_url:(o=(i=this.globalScope)===null||i===void 0?void 0:i.document)===null||o===void 0?void 0:o.referrer.replace(/\/$/,""),cookie:this.getCookie(),browser:this.getBrowser(this.ua),landing_url:this.getLandingUrl(),first_seen:this.getFirstSeen(),url_param:this.getUrlParam(),user_agent:this.userAgent},s)},e.prototype.getLanguage=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},e.prototype.getOs=function(t){var r,n;return[(r=t.browser)===null||r===void 0?void 0:r.name,(n=t.browser)===null||n===void 0?void 0:n.major].filter(function(i){return i!=null}).join(" ")},e.prototype.getDeviceModel=function(t){var r;return(r=t.os)===null||r===void 0?void 0:r.name},e.prototype.getBrowser=function(t){var r,n=(r=t.browser)===null||r===void 0?void 0:r.name;return n?.includes("Chrom")&&(n="Chrome"),n?.includes("Firefox")&&(n="Firefox"),n?.includes("Safari")&&(n="Safari"),n?.includes("Edge")&&(n="Edge"),n?.includes("Opera")&&(n="Opera"),n},e.prototype.getCookie=function(){var t,r,n,i,o;if(!((r=(t=this.globalScope)===null||t===void 0?void 0:t.document)===null||r===void 0)&&r.cookie)return Object.fromEntries((o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.document)===null||i===void 0?void 0:i.cookie)===null||o===void 0?void 0:o.split("; ").map(function(s){return s.split("=")}))},e.prototype.getLandingUrl=function(){var t,r;try{var n=JSON.parse(this.sessionStorage.get(this.storageKey)||"{}");return n.landing_url||(n.landing_url=(r=(t=this.globalScope)===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.href.replace(/\/$/,""),this.sessionStorage.put(this.storageKey,JSON.stringify(n))),n.landing_url}catch{return}},e.prototype.getFirstSeen=function(){try{var t=JSON.parse(this.localStorage.get(this.storageKey)||"{}");return t.first_seen||(t.first_seen=(Date.now()/1e3).toString(),this.localStorage.put(this.storageKey,JSON.stringify(t))),t.first_seen}catch{return}},e.prototype.getUrlParam=function(){var t,r,n;if(this.globalScope){var i={};try{var o=new URL(this.globalScope.location.href);try{for(var s=Fr(o.searchParams),a=s.next();!a.done;a=s.next()){var u=ct(a.value,2),c=u[0],l=u[1];i[c]=Rt(Rt([],ct((n=i[c])!==null&&n!==void 0?n:[]),!1),ct(l.split(",")),!1)}}catch(f){t={error:f}}finally{try{a&&!a.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}}catch{return}return Object.entries(i).reduce(function(f,d){var h=ct(d,2),v=h[0],p=h[1];return f[v]=p.length==1?p[0]:p,f},{})}},e})();Ge.experimentInstances={};var wh=Ge.experimentInstances,jO=function(e,t){var r=function(){return new lO(e,Ml.getInstance(ty(t)),1e4)};return $O(e,t,r)},ty=function(e){return e?.instanceName||Pn.instanceName},qO=function(e,t){var r=ty(t),n=t?.internalInstanceNameSuffix;return n?"".concat(r,".").concat(e,".").concat(n):"".concat(r,".").concat(e)},VO=function(e,t){return new HO(e,Te(Te({},t),{userProvider:new BO(t?.userProvider,e)}))},$O=function(e,t,r){var n=qO(e,t),i=wh[n];return i||(i=VO(e,t),r&&i.addPlugin(r()),wh[n]=i,i)},Qo=(function(){function e(t){this.name=e.pluginName,this.config=t}return e.prototype.setup=function(t,r){var n;return x(this,void 0,void 0,function(){return O(this,function(i){return this.experiment=jO(((n=this.config)===null||n===void 0?void 0:n.deploymentKey)||t.apiKey,this.config),[2]})})},e.pluginName="@amplitude/experiment-analytics-plugin",e})(),WO=function(e){return new Qo(e)},GO=(function(){function e(){}return e.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:KO(),platform:"Web",os:void 0,deviceModel:void 0}},e})(),KO=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},zO=(function(){function e(){this.queue=[]}return e.prototype.logEvent=function(t){this.receiver?this.receiver(t):this.queue.length<512&&this.queue.push(t)},e.prototype.setEventReceiver=function(t){this.receiver=t,this.queue.length>0&&(this.queue.forEach(function(r){t(r)}),this.queue=[])},e})(),Or=function(){return Or=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Th(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}var es=function(e,t){var r,n,i=["string","number","boolean","undefined"],o=typeof e,s=typeof t;if(o!==s)return!1;try{for(var a=Zo(i),u=a.next();!u.done;u=a.next()){var c=u.value;if(c===o)return e===t}}catch(b){r={error:b}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}if(e==null&&t==null)return!0;if(e==null||t==null||e.length!==t.length)return!1;var l=Array.isArray(e),f=Array.isArray(t);if(l!==f)return!1;if(l&&f){for(var d=0;d{if(f=!0,_&&(clearTimeout(_),_=null),d._q&&d._q.length>0)for(console.warn(`Engagement SDK failed to load within ${A}ms. Resolving pending calls gracefully.`);d._q.length>0;){let C=d._q.shift();if(!C)continue;let I=C[0],P=Ah.includes(I);if(console.warn(`Engagement SDK method '${I}' still in queue (isAsyncMethod=${P}); attempting to resolve as no-op.`),P&&C[1]instanceof Function&&C[2]instanceof Function){let R=C[1];console.warn(`Engagement SDK method '${I}' resolved as no-op due to script loading failure`),R(void 0)}}};e(y,d._configuration.options.splitting?"module":void 0,p?.nonce,S);let A=1e4;_=setTimeout(()=>{S()},A)},plugin(v){let p=d.init;return{name:"@amplitude/engagement-browser",type:"enrichment",async setup(b,E){var m;let g=(m=b.instanceName)!=null?m:ex,y=ZO.getInstance(g).identityStore;p(b.apiKey,{serverZone:b.serverZone,...v,options:{logLevel:b.logLevel,logger:b.loggerProvider,...v?.options}});let _=[{track:S=>{E.track(S)}}];await window.engagement.boot({user:()=>{let S=y.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:S.userProperties,getSessionId:E.getSessionId}},integrations:_}),y.addIdentityListener(S=>{var A,C,I,P;if(!((A=window.engagement)!=null&&A._.user)||!((C=window.engagement)!=null&&C._analytics.hasBooted)){console.warn("Engagement SDK not booted. Ignoring identity change.");return}((P=(I=window.engagement)==null?void 0:I._.user)==null?void 0:P.user_id)!==S.userId?(window.engagement.shutdown(),window.engagement.boot({user:()=>{let R=y.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:R.userProperties,getSessionId:E.getSessionId}},integrations:_})):window.engagement._setUserProperties(S.userProperties)})},async execute(b){return window.engagement.forwardEvent(b),b}}}},h=d;return new Proxy(d,{get:function(v,p){if(p in h)return h[p];if(p!=="then")return p==="gs"||p==="rc"?new Proxy({},{get:function(b,E){return function(){let m=Array.from(arguments),g=`${p}.${E}`;m.unshift(g),d._q.push(m)}}}):Ah.includes(p)?function(){let b=Array.prototype.slice.call(arguments);return new Promise((E,m)=>{b.unshift(p,E,m),d._q.push(b),f&&E(void 0)})}:function(){let b=Array.prototype.slice.call(arguments);b.unshift(p),d._q.push(b)}}})}var ry=(e=>(e.Local="https://local.amplitude.com:3010/",e.Staging="https://apps.stag2.amplitude.com/",e.Production="https://app.amplitude.com/",e.ProductionEU="https://app.eu.amplitude.com/",e))(ry||{}),rx=e=>{if(typeof window<"u"&&window.opener)for(let t of Object.values(ry))window.opener.postMessage({message:e},t)};function nx(e,t,r,n){for(let i of e){let o=document.createElement("script");o.src=i,o.id="engagement-sdk-bundle",r&&o.setAttribute("nonce",r),t&&o.setAttribute("type",t),n&&(o.onerror=n),document.getElementsByTagName("head")[0].appendChild(o)}}var ix=()=>(rx("ENGAGEMENT_SNIPPET"),tx((e,t,r,n)=>nx([e||"https://cdn.amplitude.com/engagement-browser/prod/index.min.js.gz"],t,r,n))),Ch=ix(),ox=e=>(typeof window<"u"&&!window.engagement&&(window.engagement=Ch),Ch.plugin(e)),sx="1.0.15",ax="amplitude-ts-unified",ux=function(){return{type:"enrichment",name:"@amplitude/unified-library-plugin",execute:function(e){var t;return x(this,void 0,void 0,function(){return O(this,function(r){return e.library="".concat(ax,"/").concat(sx,"-").concat((t=e.library)!==null&&t!==void 0?t:""),[2,e]})})}}},lx=(function(e){Dt(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.sessionReplay=function(){return this._sessionReplay},t.prototype.experiment=function(){if(this.config!==void 0){var r=this.plugins(Qo);if(r.length===0){this.config.loggerProvider.debug("".concat(Qo.pluginName," plugin is not found."));return}else{if(r.length===1)return r[0].experiment;this.config.loggerProvider.debug("Multiple instances of ".concat(Qo.pluginName," are found."));return}}},t.prototype.initAll=function(r,n){return x(this,void 0,void 0,function(){var i=this;return O(this,function(o){return this._initAllPromise?[2,this._initAllPromise]:(this._initAllPromise=(function(){return x(i,void 0,void 0,function(){var s,a;return O(this,function(u){switch(u.label){case 0:return s={serverZone:n?.serverZone,instanceName:n?.instanceName},e.prototype.add.call(this,ux()),[4,e.prototype.init.call(this,r,D(D({},n?.analytics),s)).promise];case 1:return u.sent(),[4,e.prototype.add.call(this,IR(D(D({},n?.sessionReplay),s))).promise];case 2:return u.sent(),[4,e.prototype.add.call(this,WO(D(D({},n?.experiment),s))).promise];case 3:return u.sent(),a=this.plugin(Qu.pluginName),a===void 0?this.config.loggerProvider.debug("".concat(Qu.pluginName," plugin is not found.")):this._sessionReplay=a.sessionReplay,[4,e.prototype.add.call(this,ox(D(D({},n?.engagement),s))).promise];case 4:return u.sent(),[2]}})})})().finally(function(){i._initAllPromise=void 0}),[2,this._initAllPromise])})})},t.prototype.init=function(r,n,i){r===void 0&&(r="");var o=e.prototype.init.call(this,r,n,i);return o},t})(Mk),ny=function(){var e=new lx;return{_setDiagnosticsSampleRate:_e(e._setDiagnosticsSampleRate.bind(e),"_setDiagnosticsSampleRate",Pe(e),Re(e,["config"])),_enableRequestBodyCompressionExperimental:_e(e._enableRequestBodyCompressionExperimental.bind(e),"_enableRequestBodyCompressionExperimental",Pe(e),Re(e,["config"])),experiment:_e(e.experiment.bind(e),"experiment",Pe(e),Re(e,["config"])),sessionReplay:_e(e.sessionReplay.bind(e),"sessionReplay",Pe(e),Re(e,["config"])),initAll:_e(e.initAll.bind(e),"initAll",Pe(e),Re(e,["config"])),init:_e(e.init.bind(e),"init",Pe(e),Re(e,["config"])),add:_e(e.add.bind(e),"add",Pe(e),Re(e,["config.apiKey","timeline.plugins"])),remove:_e(e.remove.bind(e),"remove",Pe(e),Re(e,["config.apiKey","timeline.plugins"])),track:_e(e.track.bind(e),"track",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),logEvent:_e(e.logEvent.bind(e),"logEvent",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),identify:_e(e.identify.bind(e),"identify",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),groupIdentify:_e(e.groupIdentify.bind(e),"groupIdentify",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),setGroup:_e(e.setGroup.bind(e),"setGroup",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),revenue:_e(e.revenue.bind(e),"revenue",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),flush:_e(e.flush.bind(e),"flush",Pe(e),Re(e,["config.apiKey","timeline.queue.length"])),getUserId:_e(e.getUserId.bind(e),"getUserId",Pe(e),Re(e,["config","config.userId"])),setUserId:_e(e.setUserId.bind(e),"setUserId",Pe(e),Re(e,["config","config.userId"])),getDeviceId:_e(e.getDeviceId.bind(e),"getDeviceId",Pe(e),Re(e,["config","config.deviceId"])),setDeviceId:_e(e.setDeviceId.bind(e),"setDeviceId",Pe(e),Re(e,["config","config.deviceId"])),reset:_e(e.reset.bind(e),"reset",Pe(e),Re(e,["config","config.userId","config.deviceId"])),getSessionId:_e(e.getSessionId.bind(e),"getSessionId",Pe(e),Re(e,["config"])),setSessionId:_e(e.setSessionId.bind(e),"setSessionId",Pe(e),Re(e,["config"])),extendSession:_e(e.extendSession.bind(e),"extendSession",Pe(e),Re(e,["config"])),setOptOut:_e(e.setOptOut.bind(e),"setOptOut",Pe(e),Re(e,["config"])),getOptOut:_e(e.getOptOut.bind(e),"getOptOut",Pe(e),Re(e,["config"])),setTransport:_e(e.setTransport.bind(e),"setTransport",Pe(e),Re(e,["config"])),getIdentity:_e(e.getIdentity.bind(e),"getIdentity",Pe(e),Re(e,["config"])),setIdentity:_e(e.setIdentity.bind(e),"setIdentity",Pe(e),Re(e,["config","config.userId","config.deviceId"]))}};const Ne=ny();var cx=Ne._setDiagnosticsSampleRate,fx=Ne._enableRequestBodyCompressionExperimental,iy=Ne.initAll,dx=Ne.experiment,hx=Ne.sessionReplay,vx=Ne.add,px=Ne.extendSession,gx=Ne.flush,mx=Ne.getDeviceId,yx=Ne.getIdentity,bx=Ne.getOptOut,Ex=Ne.getSessionId,_x=Ne.getUserId,Sx=Ne.groupIdentify,wx=Ne.identify,Tx=Ne.logEvent,Ax=Ne.remove,Cx=Ne.reset,Ix=Ne.revenue,kx=Ne.setDeviceId,Px=Ne.setGroup,Rx=Ne.setIdentity,Ox=Ne.setOptOut,xx=Ne.setSessionId,Nx=Ne.setTransport,Lx=Ne.setUserId,Mx=Ne.track;const Dx=Object.freeze(Object.defineProperty({__proto__:null,Identify:Fn,Revenue:Gp,Types:bC,_enableRequestBodyCompressionExperimental:fx,_setDiagnosticsSampleRate:cx,add:vx,createInstance:ny,experiment:dx,extendSession:px,flush:gx,getDeviceId:mx,getIdentity:yx,getOptOut:bx,getSessionId:Ex,getUserId:_x,groupIdentify:Sx,identify:wx,initAll:iy,logEvent:Tx,remove:Ax,reset:Cx,revenue:Ix,sessionReplay:hx,setDeviceId:kx,setGroup:Px,setIdentity:Rx,setOptOut:Ox,setSessionId:xx,setTransport:Nx,setUserId:Lx,track:Mx},Symbol.toStringTag,{value:"Module"})),Ux=rr(e=>{const r=ro().public.amplitudeApiKey;return r&&iy(r),{provide:{amplitude:Dx}}}),Fx=[fS,gS,Vw,Ww,Gw,Kw,Xw,Yw,Qw,Ux],Lo="
",Ga=new WeakMap;function Hx(e){if(Ga.has(e))return Ga.get(e);const t={...e};return t.render?t.render=(r,n,i,o,s,a)=>{if(o.mounted$??r.mounted$){const u=e.render?.bind(r)(r,n,i,o,s,a);return u.children===null||typeof u.children=="string"?xt(u):Ke(u)}return Ea(r._.vnode.el,Lo)}:t.template&&=` - - - `,t.setup=(r,n)=>{const i=He(),o=br(i.isHydrating===!1),s=hn();if(i.isHydrating){const u={...s.attrs},c=Bx(s);for(const l in u)delete s.attrs[l];Vn(()=>{Object.assign(s.attrs,u),s.vnode.dirs=c})}Vn(()=>{o.value=!0});const a=e.setup?.(r,n)||{};return al(a)?Promise.resolve(a).then(u=>typeof u!="function"?(u||={},u.mounted$=o,u):(...c)=>{if(o.value||!i.isHydrating){const l=u(...c);return l.children===null||typeof l.children=="string"?xt(l):Ke(l)}return Ea(s?.vnode.el,Lo)}):typeof a=="function"?(...u)=>{if(o.value){const c=a(...u),l=t.inheritAttrs!==!1?n.attrs:void 0;return c.children===null||typeof c.children=="string"?xt(c,l):Ke(c,l)}return Ea(s?.vnode.el,Lo)}:Object.assign(a,{mounted$:o})},Ga.set(e,t),t}function Bx(e){if(!e||!e.vnode.dirs)return null;const t=e.vnode.dirs;return e.vnode.dirs=null,t}function jx(e={}){const t=br(""),r=br(e.politeness||"polite"),n=_p();function i(c="",l="polite"){t.value=c,r.value=l}function o(c){return i(c,"polite")}function s(c){return i(c,"assertive")}function a(){i(document?.title?.trim(),r.value)}function u(){n?.hooks?.removeHook("dom:rendered",a)}return a(),n?.hooks?.hook("dom:rendered",()=>{a()}),{_cleanup:u,message:t,politeness:r,set:i,polite:o,assertive:s}}function qx(e={}){const t=He(),r=t._routeAnnouncer||=jx(e);return e.politeness!==r.politeness.value&&(r.politeness.value=e.politeness||"polite"),ll()&&(t._routeAnnouncerDeps||=0,t._routeAnnouncerDeps++,Ey(()=>{t._routeAnnouncerDeps--,t._routeAnnouncerDeps===0&&(r._cleanup(),delete t._routeAnnouncer)})),r}const Vx=jr({name:"NuxtRouteAnnouncer",props:{atomic:{type:Boolean,default:!1},politeness:{type:String,default:"polite"}},setup(e,{slots:t,expose:r}){const{set:n,polite:i,assertive:o,message:s,politeness:a}=qx({politeness:e.politeness});return r({set:n,polite:i,assertive:o,message:s,politeness:a}),()=>Ke("span",{class:"nuxt-route-announcer",style:{position:"absolute"}},Ke("span",{role:"alert","aria-live":a.value,"aria-atomic":e.atomic,style:{border:"0",clip:"rect(0 0 0 0)","clip-path":"inset(50%)",height:"1px",width:"1px",overflow:"hidden",position:"absolute","white-space":"nowrap","word-wrap":"normal",margin:"-1px",padding:"0"}},t.default?t.default({message:s.value}):s.value))}}),$x=(...e)=>e.find(t=>t!==void 0);function Wx(e){const t=e.componentName||"NuxtLink";function r(o){return typeof o=="string"&&o.startsWith("#")}function n(o,s,a){const u=a??e.trailingSlash;if(!o||u!=="append"&&u!=="remove")return o;if(typeof o=="string")return Mo(o,u);const c="path"in o&&o.path!==void 0?o.path:s(o).path;return{...o,name:void 0,path:Mo(c,u)}}function i(o){const s=wt(),a=ro(),u=We(()=>!!o.target&&o.target!=="_self"),c=We(()=>{const b=o.to||o.href||"";return typeof b=="string"&&qr(b,{acceptRelative:!0})}),l=gc("RouterLink"),f=l&&typeof l!="string"?l.useLink:void 0,d=We(()=>{if(o.external)return!0;const b=o.to||o.href||"";return typeof b=="object"?!1:b===""||c.value}),h=We(()=>{const b=o.to||o.href||"";return d.value?b:n(b,s.resolve,o.trailingSlash)}),v=d.value?void 0:f?.({...o,to:h}),p=We(()=>{const b=o.trailingSlash??e.trailingSlash;if(!h.value||c.value||r(h.value))return h.value;if(d.value){const E=typeof h.value=="object"&&"path"in h.value?vu(h.value):h.value,m=typeof E=="object"?s.resolve(E).href:E;return Mo(m,b)}return typeof h.value=="object"?s.resolve(h.value)?.href??null:Mo(Al(a.app.baseURL,h.value),b)});return{to:h,hasTarget:u,isAbsoluteUrl:c,isExternal:d,href:p,isActive:v?.isActive??We(()=>h.value===s.currentRoute.value.path),isExactActive:v?.isExactActive??We(()=>h.value===s.currentRoute.value.path),route:v?.route??We(()=>s.resolve(h.value)),async navigate(b){await f_(p.value,{replace:o.replace,external:d.value||u.value})}}}return jr({name:t,props:{to:{type:[String,Object],default:void 0,required:!1},href:{type:[String,Object],default:void 0,required:!1},target:{type:String,default:void 0,required:!1},rel:{type:String,default:void 0,required:!1},noRel:{type:Boolean,default:void 0,required:!1},prefetch:{type:Boolean,default:void 0,required:!1},prefetchOn:{type:[String,Object],default:void 0,required:!1},noPrefetch:{type:Boolean,default:void 0,required:!1},activeClass:{type:String,default:void 0,required:!1},exactActiveClass:{type:String,default:void 0,required:!1},prefetchedClass:{type:String,default:void 0,required:!1},replace:{type:Boolean,default:void 0,required:!1},ariaCurrentValue:{type:String,default:void 0,required:!1},external:{type:Boolean,default:void 0,required:!1},custom:{type:Boolean,default:void 0,required:!1},trailingSlash:{type:String,default:void 0,required:!1}},useLink:i,setup(o,{slots:s}){const a=wt(),{to:u,href:c,navigate:l,isExternal:f,hasTarget:d,isAbsoluteUrl:h}=i(o),v=br(!1),p=Qt(null),b=g=>{p.value=o.custom?g?.$el?.nextElementSibling:g?.$el};function E(g){return!v.value&&(typeof o.prefetchOn=="string"?o.prefetchOn===g:o.prefetchOn?.[g]??e.prefetchOn?.[g])&&(o.prefetch??e.prefetch)!==!1&&o.noPrefetch!==!0&&o.target!=="_blank"&&!Xx()}async function m(g=He()){if(v.value)return;v.value=!0;const y=typeof u.value=="string"?u.value:f.value?vu(u.value):a.resolve(u.value).fullPath,_=f.value?new URL(y,window.location.href).href:y;await Promise.all([g.hooks.callHook("link:prefetch",_).catch(()=>{}),!f.value&&!d.value&&Bp(u.value,a).catch(()=>{})])}if(E("visibility")){const g=He();let y,_=null;Vn(()=>{const S=Kx();Hs(()=>{y=Au(()=>{p?.value?.tagName&&(_=S.observe(p.value,async()=>{_?.(),_=null,await m(g)}))})})}),ei(()=>{y&&$w(y),_?.(),_=null})}return()=>{if(!f.value&&!d.value&&!r(u.value)){const _={ref:b,to:u.value,activeClass:o.activeClass||e.activeClass,exactActiveClass:o.exactActiveClass||e.exactActiveClass,replace:o.replace,ariaCurrentValue:o.ariaCurrentValue,custom:o.custom};return o.custom||(E("interaction")&&(_.onPointerenter=m.bind(null,void 0),_.onFocus=m.bind(null,void 0)),v.value&&(_.class=o.prefetchedClass||e.prefetchedClass),_.rel=o.rel||void 0),Ke(gc("RouterLink"),_,s.default)}const g=o.target||null,y=$x(o.noRel?"":o.rel,e.externalRelAttribute,h.value||d.value?"noopener noreferrer":"")||null;return o.custom?s.default?s.default({href:c.value,navigate:l,prefetch:m,get route(){if(!c.value)return;const _=new URL(c.value,window.location.href);return{path:_.pathname,fullPath:_.pathname,get query(){return Tl(_.search)},hash:_.hash,params:{},name:void 0,matched:[],redirectedFrom:void 0,meta:{},href:c.value}},rel:y,target:g,isExternal:f.value||d.value,isActive:!1,isExactActive:!1}):null:Ke("a",{ref:p,href:c.value||null,rel:y,target:g,onClick:_=>{if(!(f.value||d.value))return _.preventDefault(),o.replace?a.replace(c.value):a.push(c.value)}},s.default?.())}}})}const Gx=Wx(zE);function Mo(e,t){const r=t==="append"?tp:zi;return qr(e)&&!e.startsWith("http")?e:r(e,!0)}function Kx(){const e=He();if(e._observer)return e._observer;let t=null;const r=new Map,n=(o,s)=>(t||=new IntersectionObserver(a=>{for(const u of a){const c=r.get(u.target);(u.isIntersecting||u.intersectionRatio>0)&&c&&c()}}),r.set(o,s),t.observe(o),()=>{r.delete(o),t?.unobserve(o),r.size===0&&(t?.disconnect(),t=null)});return e._observer={observe:n}}const zx=/2g/;function Xx(){const e=navigator.connection;return!!(e&&(e.saveData||zx.test(e.effectiveType)))}const Yx="$s";function Jx(...e){const t=typeof e[e.length-1]=="string"?e.pop():void 0;typeof e[0]!="string"&&e.unshift(t);const[r,n]=e;if(!r||typeof r!="string")throw new TypeError("[nuxt] [useState] key must be a string: "+r);if(n!==void 0&&typeof n!="function")throw new Error("[nuxt] [useState] init must be a function: "+n);const i=Yx+r,o=He(),s=nv(o.payload.state,i);if(s.value===void 0&&n){const a=n();if(Ve(a))return o.payload.state[i]=a,a;s.value=a}return s}const Do=new Map,Qx=()=>{const e=Jx("auth-user",()=>{if(typeof window<"u"){const o=localStorage.getItem("currentUser");if(o){const s=Do.get(o);if(s)return s}}return null});return{user:e,login:async(o,s)=>{if(!o||!s)return!1;try{const a=await $fetch("/api/auth/login",{method:"POST",body:{username:o,password:s}});return a.success&&a.user?(e.value=a.user,Do.set(o,a.user),typeof window<"u"&&localStorage.setItem("currentUser",o),!0):!1}catch(a){return console.error("Login error:",a),!1}},logout:()=>{e.value=null,typeof window<"u"&&localStorage.removeItem("currentUser")},setUser:o=>{e.value=o,Do.set(o.username,o)},incrementBurritoConsiderations:()=>{e.value&&(e.value.burritoConsiderations++,Do.set(e.value.username,e.value),e.value={...e.value})}}},Zx={class:"header"},eN={class:"header-container"},tN={class:"user-section"},rN={key:1},nN=jr({__name:"Header",setup(e){const t=Qx(),r=We(()=>t.user.value),{$amplitude:n}=He(),i=()=>{n?.track("user_logged_out"),n?.reset(),t.logout()};return(o,s)=>{const a=Gx;return bt(),Cn("header",Zx,[xr("div",eN,[xr("nav",null,[De(a,{to:"/"},{default:Ai(()=>[...s[0]||(s[0]=[Pi("Home",-1)])]),_:1}),ke(r)?(bt(),Cn(lt,{key:0},[De(a,{to:"/burrito"},{default:Ai(()=>[...s[1]||(s[1]=[Pi("Burrito Consideration",-1)])]),_:1}),De(a,{to:"/profile"},{default:Ai(()=>[...s[2]||(s[2]=[Pi("Profile",-1)])]),_:1})],64)):Z0("",!0)]),xr("div",tN,[ke(r)?(bt(),Cn(lt,{key:0},[xr("span",null,"Welcome, "+Fh(ke(r).username)+"!",1),xr("button",{onClick:i,class:"btn-logout"}," Logout ")],64)):(bt(),Cn("span",rN,"Not logged in"))])])])}}}),oy=(e="RouteProvider")=>jr({name:e,props:{route:{type:Object,required:!0},vnode:Object,vnodeRef:Object,renderKey:String,trackRootNodes:Boolean},setup(t){const r=t.renderKey,n=t.route,i={};for(const o in t.route)Object.defineProperty(i,o,{get:()=>r===t.renderKey?t.route[o]:n[o],enumerable:!0});return Ln(Ds,hr(i)),()=>t.vnode?Ke(t.vnode,{ref:t.vnodeRef}):t.vnode}}),iN=oy(),Ih=new WeakMap,oN=jr({name:"NuxtPage",inheritAttrs:!1,props:{name:{type:String},transition:{type:[Boolean,Object],default:void 0},keepalive:{type:[Boolean,Object],default:void 0},route:{type:Object},pageKey:{type:[Function,String],default:null}},setup(e,{attrs:t,slots:r,expose:n}){const i=He(),o=Qt(),s=_t(Ds,null);let a;n({pageRef:o});const u=_t(u_,null);let c;const l=i.deferHydration();let f=!1,d=0;if(i.isHydrating){const v=i.hooks.hookOnce("app:error",l);wt().beforeEach(v)}e.pageKey&&Mn(()=>e.pageKey,(v,p)=>{v!==p&&i.callHook("page:loading:start")});let h=!1;{const v=wt().beforeResolve(()=>{h=!1});ei(()=>{v()})}return()=>Ke(Up,{name:e.name,route:e.route,...t},{default:v=>{const p=aN(s,v.route,v.Component),b=s&&s.matched.length===v.route.matched.length;if(!v.Component){if(c&&!b)return c;l();return}if(c&&u&&!u.isCurrent(v.route))return c;if(p&&s&&(!u||u?.isCurrent(s)))return b?c:null;const E=wu(v,e.pageKey),m=uN(s,v.route,v.Component);!i.isHydrating&&a===E&&!m&&Vi(()=>{h||(h=!0,i.callHook("page:loading:end"))}),f&&a!==E&&d++,a=E;const g=!!(e.transition??v.route.meta.pageTransition??Zc),y=g&&sN([e.transition,v.route.meta.pageTransition,Zc,{onAfterLeave(){delete i._runningTransition,i.callHook("page:transition:finish",v.Component)}}]),_=e.keepalive??v.route.meta.keepalive??KE;return c=Pw(g&&y,Cw(_,Ke(jv,{key:d,suspensible:!0,onPending:()=>{f=!0,g&&(i._runningTransition=!0),i.callHook("page:start",v.Component)},onResolve:async()=>{f=!1;try{await Vi(),i._route.sync?.(),await i.callHook("page:finish",v.Component),delete i._runningTransition,!h&&!m&&(h=!0,await i.callHook("page:loading:end"))}finally{l()}}},{default:()=>{const S={key:E||void 0,vnode:r.default?lN(r.default,v):v.Component,route:v.route,renderKey:E||void 0,trackRootNodes:g,vnodeRef:o};if(!_)return Ke(iN,S);const A=v.Component.type,C=A;let I=Ih.get(C);return I||(I=oy(A.name||A.__name),Ih.set(C,I)),Ke(I,S)}}))).default(),c}})}});function sN(e){const t=[];for(const r of e)r&&t.push({...r,onAfterLeave:r.onAfterLeave?xl(r.onAfterLeave):void 0});return hp(...t)}function aN(e,t,r){if(!e)return!1;const n=t.matched.findIndex(i=>i.components?.default===r?.type);return!n||n===-1?!1:t.matched.slice(0,n).some((i,o)=>i.components?.default!==e.matched[o]?.components?.default)||r&&wu({route:t,Component:r})!==wu({route:e,Component:r})}function uN(e,t,r){return e?t.matched.findIndex(i=>i.components?.default===r?.type){const r=e.__vccOpts||e;for(const[n,i]of t)r[n]=i;return r},fN=Hx(Vx),dN={};function hN(e,t){const r=fN,n=nN,i=oN;return bt(),Cn("div",null,[De(r),De(n),xr("main",null,[De(i)])])}const vN=cN(dN,[["render",hN]]),pN={__name:"nuxt-error-page",props:{error:Object},setup(e){const r=e.error,n=Number(r.statusCode||500),i=n===404,o=r.statusMessage??(i?"Page Not Found":"Internal Server Error"),s=r.message||r.toString(),a=void 0,l=i?pc(()=>Yt(()=>import("./MzFTpsyS.js"),__vite__mapDeps([0,1]),import.meta.url)):pc(()=>Yt(()=>import("./z593H3Qg.js"),__vite__mapDeps([2,3]),import.meta.url));return(f,d)=>(bt(),Qr(ke(l),gy(Kv({status:ke(n),statusText:ke(o),statusCode:ke(n),statusMessage:ke(o),description:ke(s),stack:ke(a)})),null,16))}},gN={key:0},kh={__name:"nuxt-root",setup(e){const t=()=>null,r=He(),n=r.deferHydration();if(r.isHydrating){const c=r.hooks.hookOnce("app:error",n);wt().beforeEach(c)}const i=!1;Ln(Ds,l_()),r.hooks.callHookWith(c=>c.map(l=>l()),"vue:setup");const o=Us(),s=!1,a=/bot\b|chrome-lighthouse|facebookexternalhit|google\b/i;Ev((c,l,f)=>{if(r.hooks.callHook("vue:error",c,l,f).catch(d=>console.error("[nuxt] Error in `vue:error` hook",d)),a.test(navigator.userAgent))return r.hooks.callHook("app:error",c),console.error(`[nuxt] Not rendering error page for bot with user agent \`${navigator.userAgent}\`:`,c),!1;if(gp(c)&&(c.fatal||c.unhandled))return r.runWithContext(()=>Yr(c)),!1});const u=!1;return(c,l)=>(bt(),Qr(jv,{onResolve:ke(n)},{default:Ai(()=>[ke(s)?(bt(),Cn("div",gN)):ke(o)?(bt(),Qr(ke(pN),{key:1,error:ke(o)},null,8,["error"])):ke(u)?(bt(),Qr(ke(t),{key:2,context:ke(u)},null,8,["context"])):ke(i)?(bt(),Qr(E0(ke(i)),{key:3})):(bt(),Qr(ke(vN),{key:4}))]),_:1},8,["onResolve"]))}};let Ph;{let e;Ph=async function(){if(e)return e;const n=!!(window.__NUXT__?.serverRendered??document.getElementById("__NUXT_DATA__")?.dataset.ssr==="true")?jb(kh):Bb(kh),i=ZE({vueApp:n});async function o(s){await i.callHook("app:error",s),i.payload.error||=an(s)}n.config.errorHandler=o,i.hook("app:suspense:resolve",()=>{n.config.errorHandler===o&&(n.config.errorHandler=void 0)});try{await r_(i,Fx)}catch(s){o(s)}try{await i.hooks.callHook("app:created",n),await i.hooks.callHook("app:beforeMount",n),n.mount(YE),await i.hooks.callHook("app:mounted",n),await Vi()}catch(s){o(s)}return n},e=Ph().catch(t=>{throw console.error("Error while mounting app:",t),t})}export{D as A,lt as F,Fn as I,cN as _,xr as a,De as b,Cn as c,Pi as d,Gx as e,jr as f,Qx as g,He as h,ke as i,mN as j,Ve as k,Z0 as l,bN as m,We as n,bt as o,wt as p,t0 as q,Qt as r,x as s,Fh as t,SN as u,yN as v,Ai as w,O as x,le as y,wm as z}; - -``` - ---- - -## .output/public/_nuxt/C_TnQn_7.js - -```js -import{f as l,g as p,p as m,q as c,c as r,a as t,d as i,t as a,i as s,F as _,l as f,o as n,n as g}from"./BMw6m4EL.js";const b={class:"container"},h={class:"stats"},y={style:{"margin-top":"2rem"}},C={key:0},k={key:1},v={key:2},Y={key:3},B={key:4},E=l({__name:"profile",setup(x){const u=p(),e=g(()=>u.user.value),d=m();return c(()=>{e.value||d.push("/")}),(V,o)=>(n(),r("div",b,[o[4]||(o[4]=t("h1",null,"User Profile",-1)),t("div",h,[o[2]||(o[2]=t("h2",null,"Your Information",-1)),t("p",null,[o[0]||(o[0]=t("strong",null,"Username:",-1)),i(" "+a(s(e)?.username),1)]),t("p",null,[o[1]||(o[1]=t("strong",null,"Burrito Considerations:",-1)),i(" "+a(s(e)?.burritoConsiderations),1)])]),t("div",y,[o[3]||(o[3]=t("h3",null,"Your Burrito Journey",-1)),s(e)?(n(),r(_,{key:0},[s(e).burritoConsiderations===0?(n(),r("p",C," You haven't considered any burritos yet. Visit the Burrito Consideration page to start! ")):s(e).burritoConsiderations===1?(n(),r("p",k," You've considered the burrito potential once. Keep going! ")):s(e).burritoConsiderations<5?(n(),r("p",v," You're getting the hang of burrito consideration! ")):s(e).burritoConsiderations<10?(n(),r("p",Y," You're becoming a burrito consideration expert! ")):(n(),r("p",B," You are a true burrito consideration master! 🌯 "))],64)):f("",!0)])]))}});export{E as default}; - -``` - ---- - -## .output/public/_nuxt/D7Bf6Say.js - -```js -var r=`var WebWorker=function(r){"use strict";var n=Uint8Array,e=Uint16Array,f=Uint32Array,t=new n([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),a=new n([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),o=new n([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),i=function(r,n){for(var t=new e(31),a=0;a<31;++a)t[a]=n+=1<>>1|(21845&h)<<1;g=(61680&(g=(52428&g)>>>2|(13107&g)<<2))>>>4|(3855&g)<<4,c[h]=((65280&g)>>>8|(255&g)<<8)>>>1}var w=function(r,n,f){for(var t=r.length,a=0,o=new e(n);a>>u]=s}else for(i=new e(t),a=0;a>>15-r[a];return i},d=new n(288);for(h=0;h<144;++h)d[h]=8;for(h=144;h<256;++h)d[h]=9;for(h=256;h<280;++h)d[h]=7;for(h=280;h<288;++h)d[h]=8;var m=new n(32);for(h=0;h<32;++h)m[h]=5;var y=w(d,9,0),M=w(m,5,0),p=function(r){return(r/8|0)+(7&r&&1)},b=function(r,t,a){(null==a||a>r.length)&&(a=r.length);var o=new(r instanceof e?e:r instanceof f?f:n)(a-t);return o.set(r.subarray(t,a)),o},C=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8},S=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8,r[f+2]|=e>>>16},x=function(r,f){for(var t=[],a=0;ag&&(g=i[a].s);var w=new e(g+1),d=A(t[c-1],w,0);if(d>f){a=0;var m=0,y=d-f,M=1<f))break;m+=M-(1<>>=y;m>0;){var b=i[a].s;w[b]=0&&m;--a){var C=i[a].s;w[C]==f&&(--w[C],++m)}d=f}return[new n(w),d]},A=function(r,n,e){return-1==r.s?Math.max(A(r.l,n,e+1),A(r.r,n,e+1)):n[r.s]=e},O=function(r){for(var n=r.length;n&&!r[--n];);for(var f=new e(++n),t=0,a=r[0],o=1,i=function(r){f[t++]=r},v=1;v<=n;++v)if(r[v]==a&&v!=n)++o;else{if(!a&&o>2){for(;o>138;o-=138)i(32754);o>2&&(i(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(i(a),--o;o>6;o-=6)i(8304);o>2&&(i(o-3<<5|8208),o=0)}for(;o--;)i(a);o=1,a=r[v]}return[f.subarray(0,t),n]},T=function(r,n){for(var e=0,f=0;f>>8,r[t+2]=255^r[t],r[t+3]=255^r[t+1];for(var a=0;a4&&!B[o[G-1]];--G);var H,K,L,Q,R=h+5<<3,V=T(v,d)+T(u,m)+s,X=T(v,b)+T(u,J)+s+14+3*G+T(P,B)+(2*P[16]+3*P[17]+7*P[18]);if(R<=V&&R<=X)return k(n,g,r.subarray(c,c+h));if(C(n,g,1+(X15&&(C(n,g,rr[q]>>>5&127),g+=rr[q]>>>12)}}}else H=y,K=d,L=M,Q=m;for(q=0;q255){nr=i[q]>>>18&31;S(n,g,H[nr+257]),g+=K[nr+257],nr>7&&(C(n,g,i[q]>>>23&31),g+=t[nr]);var er=31&i[q];S(n,g,L[er]),g+=Q[er],er>3&&(S(n,g,i[q]>>>5&8191),g+=a[er])}else S(n,g,H[i[q]]),g+=K[i[q]];return S(n,g,H[256]),g+K[256]},J=new f([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),N=function(r,o,i,v,u){return function(r,o,i,v,u,c){var h=r.length,g=new n(v+h+5*(1+Math.floor(h/7e3))+u),w=g.subarray(v,g.length-u),d=0;if(!o||h<8)for(var m=0;m<=h;m+=65535){var y=m+65535;y>>13,S=8191&M,x=(1<7e3||P>24576)&&H>423){d=E(r,w,0,D,I,W,j,P,z,m-z,d),P=_=j=0,z=m;for(var K=0;K<286;++K)I[K]=0;for(K=0;K<30;++K)W[K]=0}var L=2,Q=0,R=S,V=F-G&32767;if(H>2&&B==U(m-V))for(var X=Math.min(C,H)-1,Y=Math.min(32767,m),Z=Math.min(258,H);V<=Y&&--R&&F!=G;){if(r[m+L]==r[m+L-V]){for(var $=0;$L){if(L=$,Q=V,$>X)break;var rr=Math.min(V,$-2),nr=0;for(K=0;Knr&&(nr=fr,G=er)}}}V+=(F=G)-(G=A[F])+32768&32767}if(Q){D[P++]=268435456|s[L]<<18|l[Q];var tr=31&s[L],ar=31&l[Q];j+=t[tr]+a[ar],++I[257+tr],++W[ar],q=m+L,++_}else D[P++]=r[m],++I[r[m]]}}d=E(r,w,c,D,I,W,j,P,z,m-z,d)}return b(g,0,v+p(d)+u)}(r,null==o.level?6:o.level,null==o.mem?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(r.length)))):12+o.mem,i,v,!0)};function U(r,n){void 0===n&&(n={});var e=function(){var r=1,n=0;return{p:function(e){for(var f=r,t=n,a=e.length,o=0;o!=a;){for(var i=Math.min(o+5552,a);o>>8<<16|(255&n)<<8|n>>>8)+2*((255&r)<<23)}}}();e.p(r);var f,t,a,o=N(r,n,2,4);return f=o,t=n.level,a=0==t?0:t<6?1:9==t?3:2,f[0]=120,f[1]=a<<6|(a?32-2*a:1),function(r,n,e){for(;e;++n)r[n]=e,e>>>=8}(o,o.length-4,e.d()),o}const D=r=>{const e={...r,v:"v1"};return function(r,n){var e="";if(!n&&"undefined"!=typeof TextDecoder)return(new TextDecoder).decode(r);for(var f=0;f>10,56320|1023&t))}return e}(U(function(r,e){var f=r.length;if(!e&&"undefined"!=typeof TextEncoder)return(new TextEncoder).encode(r);for(var t=new n(r.length+(r.length>>>1)),a=0,o=function(r){t[a++]=r},i=0;it.length){var v=new n(a+8+(f-i<<1));v.set(t),t=v}var u=r.charCodeAt(i);u<128||e?o(u):u<2048?(o(192|u>>>6),o(128|63&u)):u>55295&&u<57344?(o(240|(u=65536+(1047552&u)|1023&r.charCodeAt(++i))>>>18),o(128|u>>>12&63),o(128|u>>>6&63),o(128|63&u)):(o(224|u>>>12),o(128|u>>>6&63),o(128|63&u))}return b(t,0,a)}(JSON.stringify(e))),!0)};onmessage=r=>{const{event:n,sessionId:e}="string"==typeof r.data?JSON.parse(r.data):r.data,f=JSON.stringify(D(n));postMessage({compressedEvent:f,sessionId:e})};const I=onmessage;return r.compressionOnMessage=I,Object.defineProperty(r,"__esModule",{value:!0}),r}({}); -`;export{r as compressionScript}; - -``` - ---- - -## .output/public/_nuxt/DfTiELZi.js - -```js -import{s as F,x as z,y as A}from"./BMw6m4EL.js";var C=10240,D=["image/","audio/","video/","application/octet-stream","font/"];function q(e){return e?D.some(function(n){return e.toLowerCase().startsWith(n)}):!1}function N(e){if(e!=null){if(typeof e=="string")return e;if(e instanceof URLSearchParams)return e.toString();if(e instanceof FormData){var n=[];return e.forEach(function(t,i){n.push("".concat(i,"=").concat(typeof t=="string"?t:"[File]"))}),n.join("&")}}}function k(e,n){if(new Blob([e]).size<=n)return{value:e,truncated:!1};for(var t=0,i=Math.min(e.length,n);t0&&e.charCodeAt(t-1)>=55296&&e.charCodeAt(t-1)<=56319&&(t-=1),{value:e.slice(0,t),truncated:!0}}var R=(function(){function e(){this.fetchObserver=null}return e.prototype.start=function(n,t){this.eventCallback=n,this.networkConfig=t,this.observeFetch()},e.prototype.stop=function(){var n;(n=this.fetchObserver)===null||n===void 0||n.call(this),this.fetchObserver=null,this.eventCallback=void 0,this.networkConfig=void 0},e.prototype.notifyEvent=function(n){var t;(t=this.eventCallback)===null||t===void 0||t.call(this,n)},e.prototype.observeFetch=function(){var n=this,t=A();if(t){var i=t.fetch;i&&(t.fetch=function(s,o){return F(n,void 0,void 0,function(){var d,r,a,h,w,u,l,v,B,_,p,l,y,g=this,m,E;return z(this,function(f){switch(f.label){case 0:d=Date.now(),r={timestamp:d,type:"fetch",method:o?.method||"GET",url:s.toString(),requestHeaders:o?.headers},a=(m=this.networkConfig)===null||m===void 0?void 0:m.body,a?.request&&(h=N(o?.body),h!==void 0&&(w=(E=a.maxBodySizeBytes)!==null&&E!==void 0?E:C,r.requestBody=k(h,w).value)),f.label=1;case 1:return f.trys.push([1,3,,4]),[4,i(s,o)];case 2:return u=f.sent(),l=Date.now(),r.status=u.status,r.duration=l-d,v={},u.headers.forEach(function(b,c){v[c]=b}),r.responseHeaders=v,a?.response?(B=v["content-type"]||null,q(B)?(r.responseBodyStatus="skipped_binary",this.notifyEvent(r)):(_=u.clone(),_.text().then(function(b){var c,T=(c=a.maxBodySizeBytes)!==null&&c!==void 0?c:C,S=k(b,T),O=S.value,x=S.truncated;r.responseBody=O,r.responseBodyStatus=x?"truncated":"captured",g.notifyEvent(r)},function(){r.responseBodyStatus="error",g.notifyEvent(r)}))):this.notifyEvent(r),[2,u];case 3:throw p=f.sent(),l=Date.now(),r.duration=l-d,y=p,r.error={name:y.name||"UnknownError",message:y.message||"An unknown error occurred"},this.notifyEvent(r),p;case 4:return[2]}})})},this.fetchObserver=function(){t.fetch=i})}},e})();export{R as NetworkObservers}; - -``` - ---- - -## .output/public/_nuxt/DMrp4Fbu.js - -```js -var rt={},on=Object.defineProperty,an=(e,t,r)=>t in e?on(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t,r)=>an(e,typeof t!="symbol"?t+"":t,r),Jr,ln=Object.defineProperty,un=(e,t,r)=>t in e?ln(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Yr=(e,t,r)=>un(e,typeof t!="symbol"?t+"":t,r),Y=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Y||{});const Qr={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},Xr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},je={};function cn(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function kr(e){if(je[e])return je[e];const t=cn(e)||globalThis[e],r=t.prototype,l=e in Qr?Qr[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in Xr?Xr[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return je[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?je[e]=p:r}catch{return r}}const Ct={};function be(e,t,r){var l;const s=`${e}.${String(r)}`;if(Ct[s])return Ct[s].call(t);const f=kr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Ct[s]=u,u.call(t)):t[r]}const xt={};function wi(e,t,r){const l=`${e}.${String(r)}`;if(xt[l])return xt[l].bind(t);const f=kr(e)[r];return typeof f!="function"?t[r]:(xt[l]=f,f.bind(t))}function hn(e){return be("Node",e,"childNodes")}function fn(e){return be("Node",e,"parentNode")}function pn(e){return be("Node",e,"parentElement")}function dn(e){return be("Node",e,"textContent")}function mn(e,t){return wi("Node",e,"contains")(t)}function gn(e){return wi("Node",e,"getRootNode")()}function yn(e){return!e||!("host"in e)?null:be("ShadowRoot",e,"host")}function wn(e){return e.styleSheets}function bn(e){return!e||!("shadowRoot"in e)?null:be("Element",e,"shadowRoot")}function Sn(e,t){return be("Element",e,"querySelector")(t)}function vn(e,t){return be("Element",e,"querySelectorAll")(t)}function Cn(){return kr("MutationObserver").constructor}const K={childNodes:hn,parentNode:fn,parentElement:pn,textContent:dn,contains:mn,getRootNode:gn,host:yn,styleSheets:wn,shadowRoot:bn,querySelector:Sn,querySelectorAll:vn,mutationObserver:Cn};function bi(e){return e.nodeType===e.ELEMENT_NODE}function Te(e){const t=e&&"host"in e&&"mode"in e&&K.host(e)||null;return!!(t&&"shadowRoot"in t&&K.shadowRoot(t)===e)}function Fe(e){return Object.prototype.toString.call(e)==="[object ShadowRoot]"}function xn(e){return e.includes(" background-clip: text;")&&!e.includes(" -webkit-background-clip: text;")&&(e=e.replace(/\sbackground-clip:\s*text;/g," -webkit-background-clip: text; background-clip: text;")),e}function Rn(e){const{cssText:t}=e;if(t.split('"').length<3)return t;const r=["@import",`url(${JSON.stringify(e.href)})`];return e.layerName===""?r.push("layer"):e.layerName&&r.push(`layer(${e.layerName})`),e.supportsText&&r.push(`supports(${e.supportsText})`),e.media.length&&r.push(e.media.mediaText),r.join(" ")+";"}function Mr(e){try{const t=e.rules||e.cssRules;if(!t)return null;let r=e.href;!r&&e.ownerNode&&e.ownerNode.ownerDocument&&(r=e.ownerNode.ownerDocument.location.href);const l=Array.from(t,s=>Si(s,r)).join("");return xn(l)}catch{return null}}function Si(e,t){if(Mn(e)){let r;try{r=Mr(e.styleSheet)||Rn(e)}catch{r=e.cssText}return e.styleSheet.href?nt(r,e.styleSheet.href):r}else{let r=e.cssText;return En(e)&&e.selectorText.includes(":")&&(r=On(r)),t?nt(r,t):r}}function On(e){const t=/(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;return e.replace(t,"$1\\$2")}function Mn(e){return"styleSheet"in e}function En(e){return"selectorText"in e}class vi{constructor(){Yr(this,"idNodeMap",new Map),Yr(this,"nodeMetaMap",new WeakMap)}getId(t){var r;return t?((r=this.getMeta(t))==null?void 0:r.id)??-1:-1}getNode(t){return this.idNodeMap.get(t)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(t){return this.nodeMetaMap.get(t)||null}removeNodeFromMap(t){const r=this.getId(t);this.idNodeMap.delete(r),t.childNodes&&t.childNodes.forEach(l=>this.removeNodeFromMap(l))}has(t){return this.idNodeMap.has(t)}hasNode(t){return this.nodeMetaMap.has(t)}add(t,r){const l=r.id;this.idNodeMap.set(l,t),this.nodeMetaMap.set(t,r)}replace(t,r){const l=this.getNode(t);if(l){const s=this.nodeMetaMap.get(l);s&&this.nodeMetaMap.set(r,s)}this.idNodeMap.set(t,r)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function In(){return new vi}function st({element:e,maskInputOptions:t,tagName:r,type:l,value:s,maskInputFn:f}){let u=s||"";const m=l&&Ce(l);return(t[r.toLowerCase()]||m&&t[m])&&(f?u=f(u,e):u="*".repeat(u.length)),u}function Ce(e){return e.toLowerCase()}const Kr="__rrweb_original__";function An(e){const t=e.getContext("2d");if(!t)return!0;const r=50;for(let l=0;la!==0))return!1}return!0}function it(e){const t=e.type;return e.hasAttribute("data-rr-is-password")?"password":t?Ce(t):null}function Ci(e,t){let r;try{r=new URL(e,t??window.location.href)}catch{return null}const l=/\.([0-9a-z]+)(?:$)/i,s=r.pathname.match(l);return s?.[1]??null}function kn(e){let t="";return e.indexOf("//")>-1?t=e.split("/").slice(0,3).join("/"):t=e.split("/")[0],t=t.split("?")[0],t}const Nn=/url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm,Pn=/^(?:[a-z+]+:)?\/\//i,_n=/^www\..*/i,Ln=/^(data:)([^,]*),(.*)/i;function nt(e,t){return(e||"").replace(Nn,(r,l,s,f,u,m)=>{const a=s||u||m,p=l||f||"";if(!a)return r;if(Pn.test(a)||_n.test(a))return`url(${p}${a}${p})`;if(Ln.test(a))return`url(${p}${a}${p})`;if(a[0]==="/")return`url(${p}${kn(t)+a}${p})`;const i=t.split("/"),c=a.split("/");i.pop();for(const o of c)o!=="."&&(o===".."?i.pop():i.push(o));return`url(${p}${i.join("/")}${p})`})}function He(e,t=!1){return t?e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,""):e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,"").replace(/0px/g,"0")}function Dn(e,t,r=!1){const l=Array.from(t.childNodes),s=[];let f=0;if(l.length>1&&e&&typeof e=="string"){let u=He(e,r);const m=u.length/e.length;for(let a=1;a2&&n[0]===""&&l[a-1].textContent!=="")d=u.indexOf(o,1);else if(n.length===1){if(o=o.substring(0,o.length-1),n=u.split(o),n.length<=1)return s.push(e),s;c=i+1}else c===p.length-1&&(d=u.indexOf(o));if(n.length>=2&&c>i){const h=l[a-1].textContent;if(h&&typeof h=="string"){const y=He(h).length;d=u.indexOf(o,y)}d===-1&&(d=n[0].length)}if(d!==-1){let h=Math.floor(d/m);for(;h>0&&h50*l.length)return s.push(e),s;const y=He(e.substring(0,h),r);if(y.length===d){s.push(e.substring(0,h)),e=e.substring(h),u=u.substring(d);break}else y.length=t.length);){let f=l(zn);if(f.slice(-1)===",")f=ke(e,f.substring(0,f.length-1)),s.push(f);else{let u="";f=ke(e,f);let m=!1;for(;;){const a=t.charAt(r);if(a===""){s.push((f+u).trim());break}else if(m)a===")"&&(m=!1);else if(a===","){r+=1,s.push((f+u).trim());break}else a==="("&&(m=!0);u+=a,r+=1}}}return s.join(", ")}const es=new WeakMap;function ke(e,t){return!t||t.trim()===""?t:Nr(e,t)}function jn(e){return!!(e.tagName==="svg"||e.ownerSVGElement)}function Nr(e,t){let r=es.get(e);if(r||(r=e.createElement("a"),es.set(e,r)),!t)t="";else if(t.startsWith("blob:")||t.startsWith("data:"))return t;return r.setAttribute("href",t),r.href}function Ri(e,t,r,l){return l&&(r==="src"||r==="href"&&!(t==="use"&&l[0]==="#")||r==="xlink:href"&&l[0]!=="#"||r==="background"&&(t==="table"||t==="td"||t==="th")?ke(e,l):r==="srcset"?qn(e,l):r==="style"?nt(l,Nr(e)):t==="object"&&r==="data"?ke(e,l):l)}function Oi(e,t,r){return(e==="video"||e==="audio")&&t==="autoplay"}function Hn(e,t,r){try{if(typeof t=="string"){if(e.classList.contains(t))return!0}else for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}if(r)return e.matches(r)}catch{}return!1}function ot(e,t,r){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return r?ot(K.parentNode(e),t,r):!1;for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}return r?ot(K.parentNode(e),t,r):!1}function Mi(e,t,r,l){let s;if(bi(e)){if(s=e,!K.childNodes(s).length)return!1}else{if(K.parentElement(e)===null)return!1;s=K.parentElement(e)}try{if(typeof t=="string"){if(l){if(s.closest(`.${t}`))return!0}else if(s.classList.contains(t))return!0}else if(ot(s,t,l))return!0;if(r){if(l){if(s.closest(r))return!0}else if(s.matches(r))return!0}}catch{}return!1}function Gn(e,t,r){const l=e.contentWindow;if(!l)return;let s=!1,f;try{f=l.document.readyState}catch{return}if(f!=="complete"){const m=setTimeout(()=>{s||(t(),s=!0)},r);e.addEventListener("load",()=>{clearTimeout(m),s=!0,t()});return}const u="about:blank";if(l.location.href!==u||e.src===u||e.src==="")return setTimeout(t,0),e.addEventListener("load",t);e.addEventListener("load",t)}function Vn(e,t,r){let l=!1,s;try{s=e.sheet}catch{return}if(s)return;const f=setTimeout(()=>{l||(t(),l=!0)},r);e.addEventListener("load",()=>{clearTimeout(f),l=!0,t()})}function Jn(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:u,inlineStylesheet:m,maskInputOptions:a={},maskTextFn:p,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o={},inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y=!1,cssCaptured:C=!1,applyBackgroundColorToBlockedElements:w=!1}=t,S=Yn(r,l);switch(e.nodeType){case e.DOCUMENT_NODE:return e.compatMode!=="CSS1Compat"?{type:Y.Document,childNodes:[],compatMode:e.compatMode}:{type:Y.Document,childNodes:[]};case e.DOCUMENT_TYPE_NODE:return{type:Y.DocumentType,name:e.name,publicId:e.publicId,systemId:e.systemId,rootId:S};case e.ELEMENT_NODE:return Xn(e,{doc:r,blockClass:s,blockSelector:f,inlineStylesheet:m,maskInputOptions:a,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o,inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y,rootId:S,applyBackgroundColorToBlockedElements:w});case e.TEXT_NODE:return Qn(e,{doc:r,needsMask:u,maskTextFn:p,rootId:S,cssCaptured:C});case e.CDATA_SECTION_NODE:return{type:Y.CDATA,textContent:"",rootId:S};case e.COMMENT_NODE:return{type:Y.Comment,textContent:K.textContent(e)||"",rootId:S};default:return!1}}function Yn(e,t){if(!t.hasNode(e))return;const r=t.getId(e);return r===1?void 0:r}function Qn(e,t){const{needsMask:r,maskTextFn:l,rootId:s,cssCaptured:f}=t,u=K.parentNode(e),m=u&&u.tagName;let a="";const p=m==="STYLE"?!0:void 0,i=m==="SCRIPT"?!0:void 0;return i?a="SCRIPT_PLACEHOLDER":f||(a=K.textContent(e),p&&a&&(a=nt(a,Nr(t.doc)))),!p&&!i&&a&&r&&(a=l?l(a,K.parentElement(e)):a.replace(/[\S]/g,"*")),{type:Y.Text,textContent:a||"",rootId:s}}function Xn(e,t){const{doc:r,blockClass:l,blockSelector:s,inlineStylesheet:f,maskInputOptions:u={},maskInputFn:m,maskAttributeFn:a,dataURLOptions:p={},inlineImages:i,recordCanvas:c,keepIframeSrcFn:o,newlyAddedElement:n=!1,rootId:d,applyBackgroundColorToBlockedElements:h=!1}=t,y=Hn(e,l,s),C=Bn(e);let w={};const S=e.attributes.length;for(let g=0;gx.href===e.href);let v=null;g&&(v=Mr(g)),v&&(delete w.rel,delete w.href,w._cssText=v)}if(C==="style"&&e.sheet){let g=Mr(e.sheet);g&&(e.childNodes.length>1&&(g=Tn(g,e)),w._cssText=g)}if(C==="input"||C==="textarea"||C==="select"){const g=e.value,v=e.checked;w.type!=="radio"&&w.type!=="checkbox"&&w.type!=="submit"&&w.type!=="button"&&g?w.value=st({element:e,type:it(e),tagName:C,value:g,maskInputOptions:u,maskInputFn:m}):v&&(w.checked=v)}if(C==="option"&&(e.selected&&!u.select?w.selected=!0:delete w.selected),C==="dialog"&&e.open&&(w.rr_open_mode=e.matches("dialog:modal")?"modal":"non-modal"),C==="canvas"&&c){if(e.__context==="2d")An(e)||(w.rr_dataURL=e.toDataURL(p.type,p.quality));else if(!("__context"in e)){const g=e.toDataURL(p.type,p.quality),v=r.createElement("canvas");v.width=e.width,v.height=e.height;const x=v.toDataURL(p.type,p.quality);g!==x&&(w.rr_dataURL=g)}}if(C==="img"&&i){Ee||(Ee=r.createElement("canvas"),Zr=Ee.getContext("2d"));const g=e,v=g.currentSrc||g.getAttribute("src")||"",x=g.crossOrigin,O=()=>{g.removeEventListener("load",O);try{Ee.width=g.naturalWidth,Ee.height=g.naturalHeight,Zr.drawImage(g,0,0),w.rr_dataURL=Ee.toDataURL(p.type,p.quality)}catch(A){if(g.crossOrigin!=="anonymous"){g.crossOrigin="anonymous",g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O);return}else console.warn(`Cannot inline img src=${v}! Error: ${A}`)}g.crossOrigin==="anonymous"&&(x?w.crossOrigin=x:g.removeAttribute("crossorigin"))};g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O)}if(C==="audio"||C==="video"){const g=w;g.rr_mediaState=e.paused?"paused":"played",g.rr_mediaCurrentTime=e.currentTime,g.rr_mediaPlaybackRate=e.playbackRate,g.rr_mediaMuted=e.muted,g.rr_mediaLoop=e.loop,g.rr_mediaVolume=e.volume}if(n||(e.scrollLeft&&(w.rr_scrollLeft=e.scrollLeft),e.scrollTop&&(w.rr_scrollTop=e.scrollTop)),y){const{width:g,height:v}=e.getBoundingClientRect();w={class:w.class,rr_width:`${g}px`,rr_height:`${v}px`,...h?{rr_background_color:Fn}:{}}}C==="iframe"&&!o(w.src)&&(e.contentDocument||(w.rr_src=w.src),delete w.src);let b;try{customElements.get(C)&&(b=!0)}catch{}return{type:Y.Element,tagName:C,attributes:w,childNodes:[],isSVG:jn(e)||void 0,needBlock:y,rootId:d,isCustom:b}}function z(e){return e==null?"":e.toLowerCase()}function Kn(e,t){if(t.comment&&e.type===Y.Comment)return!0;if(e.type===Y.Element){if(t.script&&(e.tagName==="script"||e.tagName==="link"&&(e.attributes.rel==="preload"||e.attributes.rel==="modulepreload")&&e.attributes.as==="script"||e.tagName==="link"&&e.attributes.rel==="prefetch"&&typeof e.attributes.href=="string"&&Ci(e.attributes.href)==="js"))return!0;if(t.headFavicon&&(e.tagName==="link"&&e.attributes.rel==="shortcut icon"||e.tagName==="meta"&&(z(e.attributes.name).match(/^msapplication-tile(image|color)$/)||z(e.attributes.name)==="application-name"||z(e.attributes.rel)==="icon"||z(e.attributes.rel)==="apple-touch-icon"||z(e.attributes.rel)==="shortcut icon")))return!0;if(e.tagName==="meta"){if(t.headMetaDescKeywords&&z(e.attributes.name).match(/^description|keywords$/))return!0;if(t.headMetaSocial&&(z(e.attributes.property).match(/^(og|twitter|fb):/)||z(e.attributes.name).match(/^(og|twitter):/)||z(e.attributes.name)==="pinterest"))return!0;if(t.headMetaRobots&&(z(e.attributes.name)==="robots"||z(e.attributes.name)==="googlebot"||z(e.attributes.name)==="bingbot"))return!0;if(t.headMetaHttpEquiv&&e.attributes["http-equiv"]!==void 0)return!0;if(t.headMetaAuthorship&&(z(e.attributes.name)==="author"||z(e.attributes.name)==="generator"||z(e.attributes.name)==="framework"||z(e.attributes.name)==="publisher"||z(e.attributes.name)==="progid"||z(e.attributes.property).match(/^article:/)||z(e.attributes.property).match(/^product:/)))return!0;if(t.headMetaVerification&&(z(e.attributes.name)==="google-site-verification"||z(e.attributes.name)==="yandex-verification"||z(e.attributes.name)==="csrf-token"||z(e.attributes.name)==="p:domain_verify"||z(e.attributes.name)==="verify-v1"||z(e.attributes.name)==="verification"||z(e.attributes.name)==="shopify-checkout-api-token"))return!0}}return!1}function Ne(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,maskTextClass:u,maskTextSelector:m,skipChild:a=!1,inlineStylesheet:p=!0,maskInputOptions:i={},maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h={},inlineImages:y=!1,recordCanvas:C=!1,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b=5e3,onStylesheetLoad:g,stylesheetLoadTimeout:v=5e3,keepIframeSrcFn:x=()=>!1,newlyAddedElement:O=!1,cssCaptured:A=!1,applyBackgroundColorToBlockedElements:M=!1}=t;let{needsMask:$}=t,{preserveWhiteSpace:k=!0}=t;$||($=Mi(e,u,m,$===void 0));const R=Jn(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,dataURLOptions:h,inlineImages:y,recordCanvas:C,keepIframeSrcFn:x,newlyAddedElement:O,cssCaptured:A,applyBackgroundColorToBlockedElements:M});if(!R)return console.warn(e,"not serialized"),null;let Z;l.hasNode(e)?Z=l.getId(e):Kn(R,d)||!k&&R.type===Y.Text&&!R.textContent.replace(/^\s+|\s+$/gm,"").length?Z=$e:Z=xi();const N=Object.assign(R,{id:Z});if(l.add(e,N),Z===$e)return null;w&&w(e);let ee=!a;if(N.type===Y.Element){ee=ee&&!N.needBlock,delete N.needBlock;const F=K.shadowRoot(e);F&&Fe(F)&&(N.isShadowHost=!0)}if((N.type===Y.Document||N.type===Y.Element)&&ee){d.headWhitespace&&N.type===Y.Element&&N.tagName==="head"&&(k=!1);const F={doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:a,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x,cssCaptured:!1,applyBackgroundColorToBlockedElements:M};if(!(N.type===Y.Element&&N.tagName==="textarea"&&N.attributes.value!==void 0)){N.type===Y.Element&&N.attributes._cssText!==void 0&&typeof N.attributes._cssText=="string"&&(F.cssCaptured=!0);for(const W of Array.from(K.childNodes(e))){const P=Ne(W,F);P&&N.childNodes.push(P)}}let Q=null;if(bi(e)&&(Q=K.shadowRoot(e)))for(const W of Array.from(K.childNodes(Q))){const P=Ne(W,F);P&&(Fe(Q)&&(P.isShadow=!0),N.childNodes.push(P))}}const H=K.parentNode(e);return H&&Te(H)&&Fe(H)&&(N.isShadow=!0),N.type===Y.Element&&N.tagName==="iframe"&&Gn(e,()=>{const F=e.contentDocument;if(F&&S){const Q=Ne(F,{doc:F,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});Q&&S(e,Q)}},b),N.type===Y.Element&&N.tagName==="link"&&typeof N.attributes.rel=="string"&&(N.attributes.rel==="stylesheet"||N.attributes.rel==="preload"&&typeof N.attributes.href=="string"&&Ci(N.attributes.href)==="css")&&Vn(e,()=>{if(g){const F=Ne(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});F&&g(e,F)}},v),N}function Zn(e,t){const{mirror:r=new vi,blockClass:l="rr-block",blockSelector:s=null,maskTextClass:f="rr-mask",maskTextSelector:u=null,inlineStylesheet:m=!0,inlineImages:a=!1,recordCanvas:p=!1,maskAllInputs:i=!1,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOM:d=!1,dataURLOptions:h,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v=()=>!1,applyBackgroundColorToBlockedElements:x=!1}=t||{};return Ne(e,{doc:e,mirror:r,blockClass:l,blockSelector:s,maskTextClass:f,maskTextSelector:u,skipChild:!1,inlineStylesheet:m,maskInputOptions:i===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:i===!1?{password:!0}:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d===!0||d==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:d==="all",headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0,headMetaVerification:!0}:d===!1?{}:d,dataURLOptions:h,inlineImages:a,recordCanvas:p,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v,newlyAddedElement:!1,applyBackgroundColorToBlockedElements:x})}function eo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function to(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Ge={exports:{}},ts;function ro(){if(ts)return Ge.exports;ts=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Ge.exports=t(),Ge.exports.createColors=t,Ge.exports}const so={},io=Object.freeze(Object.defineProperty({__proto__:null,default:so},Symbol.toStringTag,{value:"Module"})),he=to(io);var Rt,rs;function Pr(){if(rs)return Rt;rs=1;let e=ro(),t=he;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Rt=r,r.default=r,Rt}var Ve={},ss;function _r(){return ss||(ss=1,Ve.isClean=Symbol("isClean"),Ve.my=Symbol("my")),Ve}var Ot,is;function Ei(){if(is)return Ot;is=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Ot=r,r.default=r,Ot}var Mt,ns;function ct(){if(ns)return Mt;ns=1;let e=Ei();function t(r,l){new e(l).stringify(r)}return Mt=t,t.default=t,Mt}var Et,os;function ht(){if(os)return Et;os=1;let{isClean:e,my:t}=_r(),r=Pr(),l=Ei(),s=ct();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Et=u,u.default=u,Et}var It,as;function ft(){if(as)return It;as=1;let e=ht();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return It=t,t.default=t,It}var At,ls;function no(){if(ls)return At;ls=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return At={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},At}var kt,us;function Ii(){if(us)return kt;us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{existsSync:r,readFileSync:l}=he,{dirname:s,join:f}=he;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return kt=m,m.default=m,kt}var Nt,cs;function pt(){if(cs)return Nt;cs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{fileURLToPath:r,pathToFileURL:l}=he,{isAbsolute:s,resolve:f}=he,{nanoid:u}=no(),m=he,a=Pr(),p=Ii(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return Nt=n,n.default=n,m&&m.registerInput&&m.registerInput(n),Nt}var Pt,hs;function Ai(){if(hs)return Pt;hs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{dirname:r,relative:l,resolve:s,sep:f}=he,{pathToFileURL:u}=he,m=pt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return Pt=i,Pt}var _t,fs;function dt(){if(fs)return _t;fs=1;let e=ht();class t extends e{constructor(l){super(l),this.type="comment"}}return _t=t,t.default=t,_t}var Lt,ps;function xe(){if(ps)return Lt;ps=1;let{isClean:e,my:t}=_r(),r=ft(),l=dt(),s=ht(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},Lt=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},Lt}var Dt,ds;function Lr(){if(ds)return Dt;ds=1;let e=xe(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Dt=l,l.default=l,Dt}var Tt,ms;function ki(){if(ms)return Tt;ms=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Tt=e,e.default=e,Tt}var Ft,gs;function Dr(){if(gs)return Ft;gs=1;let e=ki();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return Ft=t,t.default=t,Ft}var Ut,ys;function oo(){if(ys)return Ut;ys=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return Ut=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},Ut}var $t,ws;function Tr(){if(ws)return $t;ws=1;let e=xe();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return $t=t,t.default=t,e.registerAtRule(t),$t}var Bt,bs;function ze(){if(bs)return Bt;bs=1;let e=xe(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Bt=l,l.default=l,e.registerRoot(l),Bt}var zt,Ss;function Ni(){if(Ss)return zt;Ss=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return zt=e,e.default=e,zt}var Wt,vs;function Fr(){if(vs)return Wt;vs=1;let e=xe(),t=Ni();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return Wt=r,r.default=r,e.registerRule(r),Wt}var qt,Cs;function ao(){if(Cs)return qt;Cs=1;let e=ft(),t=oo(),r=dt(),l=Tr(),s=ze(),f=Fr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},Ht=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),Ht}var Gt,Os;function lo(){if(Os)return Gt;Os=1;let e=Ai(),t=ct(),r=Ur();const l=Dr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return Gt=s,s.default=s,Gt}var Vt,Ms;function uo(){if(Ms)return Vt;Ms=1;let e=lo(),t=Pi(),r=Lr(),l=ze();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return Vt=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),Vt}var Jt,Es;function co(){if(Es)return Jt;Es=1;let e=ft(),t=Ii(),r=dt(),l=Tr(),s=pt(),f=ze(),u=Fr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Jt=m,m.default=m,Jt}var Yt,Is;function ho(){if(Is)return Yt;Is=1;let e=Pr(),t=ft(),r=Pi(),l=xe(),s=uo(),f=ct(),u=co(),m=Lr(),a=ki(),p=dt(),i=Tr(),c=Dr(),o=pt(),n=Ur(),d=Ni(),h=Fr(),y=ze(),C=ht();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),Yt=w,w.default=w,Yt}var fo=ho();const q=eo(fo);q.stringify;q.fromJSON;q.plugin;q.parse;q.list;q.document;q.comment;q.atRule;q.rule;q.decl;q.root;q.CssSyntaxError;q.Declaration;q.Container;q.Processor;q.Document;q.Comment;q.Warning;q.AtRule;q.Result;q.Input;q.Rule;q.Root;q.Node;var po=Object.defineProperty,mo=(e,t,r)=>t in e?po(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,oe=(e,t,r)=>mo(e,typeof t!="symbol"?t+"":t,r);function go(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function yo(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Je={exports:{}},As;function wo(){if(As)return Je.exports;As=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Je.exports=t(),Je.exports.createColors=t,Je.exports}const bo={},So=Object.freeze(Object.defineProperty({__proto__:null,default:bo},Symbol.toStringTag,{value:"Module"})),fe=yo(So);var Qt,ks;function $r(){if(ks)return Qt;ks=1;let e=wo(),t=fe;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Qt=r,r.default=r,Qt}var Ye={},Ns;function Br(){return Ns||(Ns=1,Ye.isClean=Symbol("isClean"),Ye.my=Symbol("my")),Ye}var Xt,Ps;function _i(){if(Ps)return Xt;Ps=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Xt=r,r.default=r,Xt}var Kt,_s;function mt(){if(_s)return Kt;_s=1;let e=_i();function t(r,l){new e(l).stringify(r)}return Kt=t,t.default=t,Kt}var Zt,Ls;function gt(){if(Ls)return Zt;Ls=1;let{isClean:e,my:t}=Br(),r=$r(),l=_i(),s=mt();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Zt=u,u.default=u,Zt}var er,Ds;function yt(){if(Ds)return er;Ds=1;let e=gt();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return er=t,t.default=t,er}var tr,Ts;function vo(){if(Ts)return tr;Ts=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return tr={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},tr}var rr,Fs;function Li(){if(Fs)return rr;Fs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{existsSync:r,readFileSync:l}=fe,{dirname:s,join:f}=fe;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return rr=m,m.default=m,rr}var sr,Us;function wt(){if(Us)return sr;Us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{fileURLToPath:r,pathToFileURL:l}=fe,{isAbsolute:s,resolve:f}=fe,{nanoid:u}=vo(),m=fe,a=$r(),p=Li(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return sr=n,n.default=n,m&&m.registerInput&&m.registerInput(n),sr}var ir,$s;function Di(){if($s)return ir;$s=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{dirname:r,relative:l,resolve:s,sep:f}=fe,{pathToFileURL:u}=fe,m=wt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return ir=i,ir}var nr,Bs;function bt(){if(Bs)return nr;Bs=1;let e=gt();class t extends e{constructor(l){super(l),this.type="comment"}}return nr=t,t.default=t,nr}var or,zs;function Re(){if(zs)return or;zs=1;let{isClean:e,my:t}=Br(),r=yt(),l=bt(),s=gt(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},or=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},or}var ar,Ws;function zr(){if(Ws)return ar;Ws=1;let e=Re(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},ar=l,l.default=l,ar}var lr,qs;function Ti(){if(qs)return lr;qs=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return lr=e,e.default=e,lr}var ur,js;function Wr(){if(js)return ur;js=1;let e=Ti();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return ur=t,t.default=t,ur}var cr,Hs;function Co(){if(Hs)return cr;Hs=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return cr=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},cr}var hr,Gs;function qr(){if(Gs)return hr;Gs=1;let e=Re();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return hr=t,t.default=t,e.registerAtRule(t),hr}var fr,Vs;function We(){if(Vs)return fr;Vs=1;let e=Re(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},fr=l,l.default=l,e.registerRoot(l),fr}var pr,Js;function Fi(){if(Js)return pr;Js=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return pr=e,e.default=e,pr}var dr,Ys;function jr(){if(Ys)return dr;Ys=1;let e=Re(),t=Fi();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return dr=r,r.default=r,e.registerRule(r),dr}var mr,Qs;function xo(){if(Qs)return mr;Qs=1;let e=yt(),t=Co(),r=bt(),l=qr(),s=We(),f=jr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},yr=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),yr}var wr,Zs;function Ro(){if(Zs)return wr;Zs=1;let e=Di(),t=mt(),r=Hr();const l=Wr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return wr=s,s.default=s,wr}var br,ei;function Oo(){if(ei)return br;ei=1;let e=Ro(),t=Ui(),r=zr(),l=We();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return br=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),br}var Sr,ti;function Mo(){if(ti)return Sr;ti=1;let e=yt(),t=Li(),r=bt(),l=qr(),s=wt(),f=We(),u=jr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Sr=m,m.default=m,Sr}var vr,ri;function Eo(){if(ri)return vr;ri=1;let e=$r(),t=yt(),r=Ui(),l=Re(),s=Oo(),f=mt(),u=Mo(),m=zr(),a=Ti(),p=bt(),i=qr(),c=Wr(),o=wt(),n=Hr(),d=Fi(),h=jr(),y=We(),C=gt();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),vr=w,w.default=w,vr}var Io=Eo();const j=go(Io);j.stringify;j.fromJSON;j.plugin;j.parse;j.list;j.document;j.comment;j.atRule;j.rule;j.decl;j.root;j.CssSyntaxError;j.Declaration;j.Container;j.Processor;j.Document;j.Comment;j.Warning;j.AtRule;j.Result;j.Input;j.Rule;j.Root;j.Node;class Gr{constructor(...t){oe(this,"parentElement",null),oe(this,"parentNode",null),oe(this,"ownerDocument"),oe(this,"firstChild",null),oe(this,"lastChild",null),oe(this,"previousSibling",null),oe(this,"nextSibling",null),oe(this,"ELEMENT_NODE",1),oe(this,"TEXT_NODE",3),oe(this,"nodeType"),oe(this,"nodeName"),oe(this,"RRNodeType")}get childNodes(){const t=[];let r=this.firstChild;for(;r;)t.push(r),r=r.nextSibling;return t}contains(t){if(t instanceof Gr){if(t.ownerDocument!==this.ownerDocument)return!1;if(t===this)return!0}else return!1;for(;t.parentNode;){if(t.parentNode===this)return!0;t=t.parentNode}return!1}appendChild(t){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(t,r){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(t){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const si={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ii={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},Qe={};function Ao(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function Vr(e){if(Qe[e])return Qe[e];const t=Ao(e)||globalThis[e],r=t.prototype,l=e in si?si[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in ii?ii[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return Qe[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?Qe[e]=p:r}catch{return r}}const Cr={};function Se(e,t,r){var l;const s=`${e}.${String(r)}`;if(Cr[s])return Cr[s].call(t);const f=Vr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Cr[s]=u,u.call(t)):t[r]}const xr={};function $i(e,t,r){const l=`${e}.${String(r)}`;if(xr[l])return xr[l].bind(t);const f=Vr(e)[r];return typeof f!="function"?t[r]:(xr[l]=f,f.bind(t))}function ko(e){return Se("Node",e,"childNodes")}function No(e){return Se("Node",e,"parentNode")}function Po(e){return Se("Node",e,"parentElement")}function _o(e){return Se("Node",e,"textContent")}function Lo(e,t){return $i("Node",e,"contains")(t)}function Do(e){return $i("Node",e,"getRootNode")()}function To(e){return!e||!("host"in e)?null:Se("ShadowRoot",e,"host")}function Fo(e){return e.styleSheets}function Uo(e){return!e||!("shadowRoot"in e)?null:Se("Element",e,"shadowRoot")}function $o(e,t){return Se("Element",e,"querySelector")(t)}function Bo(e,t){return Se("Element",e,"querySelectorAll")(t)}function Bi(){return Vr("MutationObserver").constructor}const L={childNodes:ko,parentNode:No,parentElement:Po,textContent:_o,contains:Lo,getRootNode:Do,host:To,styleSheets:Fo,shadowRoot:Uo,querySelector:$o,querySelectorAll:Bo,mutationObserver:Bi};function te(e,t,r=document){const l={capture:!0,passive:!0};return r.addEventListener(e,t,l),()=>r.removeEventListener(e,t,l)}const Ie=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let ni={map:{},getId(){return console.error(Ie),-1},getNode(){return console.error(Ie),null},removeNodeFromMap(){console.error(Ie)},has(){return console.error(Ie),!1},reset(){console.error(Ie)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(ni=new Proxy(ni,{get(e,t,r){return t==="map"&&console.error(Ie),Reflect.get(e,t,r)}}));function Be(e,t,r={}){let l=null,s=0;return function(...f){const u=Date.now();!s&&r.leading===!1&&(s=u);const m=t-(u-s),a=this;m<=0||m>t?(l&&(clearTimeout(l),l=null),s=u,e.apply(a,f)):!l&&r.trailing!==!1&&(l=setTimeout(()=>{s=r.leading===!1?0:Date.now(),l=null,e.apply(a,f)},m))}}function St(e,t,r,l,s=window){const f=s.Object.getOwnPropertyDescriptor(e,t);return s.Object.defineProperty(e,t,l?r:{set(u){setTimeout(()=>{r.set.call(this,u)},0),f&&f.set&&f.set.call(this,u)}}),()=>St(e,t,f||{},!0)}function Le(e,t,r){try{if(!(t in e))return()=>{};const l=e[t],s=r(l);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:l}})),e[t]=s,()=>{e[t]=l}}catch{return()=>{}}}let at=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(at=()=>new Date().getTime());function zi(e){var t,r,l,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((t=L.parentElement(f.body))==null?void 0:t.scrollLeft)||((r=f?.body)==null?void 0:r.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((l=L.parentElement(f.body))==null?void 0:l.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Wi(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function qi(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function ji(e){return e?e.nodeType===e.ELEMENT_NODE?e:L.parentElement(e):null}function re(e,t,r,l){if(!e)return!1;const s=ji(e);if(!s)return!1;try{if(typeof t=="string"){if(s.classList.contains(t)||l&&s.closest("."+t)!==null)return!0}else if(ot(s,t,l))return!0}catch{}return!!(r&&(s.matches(r)||l&&s.closest(r)!==null))}function zo(e,t){return t.getId(e)!==-1}function Rr(e,t,r){return e.tagName==="TITLE"&&r.headTitleMutations?!0:t.getId(e)===$e}function Hi(e,t){if(Te(e))return!1;const r=t.getId(e);if(!t.has(r))return!0;const l=L.parentNode(e);return l&&l.nodeType===e.DOCUMENT_NODE?!1:l?Hi(l,t):!0}function Er(e){return!!e.changedTouches}function Wo(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function Gi(e,t){return!!(e.nodeName==="IFRAME"&&t.getMeta(e))}function Vi(e,t){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&t.getMeta(e))}function Ir(e){return e?e instanceof Gr&&"shadowRoot"in e?!!e.shadowRoot:!!L.shadowRoot(e):!1}class qo{constructor(){I(this,"id",1),I(this,"styleIDMap",new WeakMap),I(this,"idStyleMap",new Map)}getId(t){return this.styleIDMap.get(t)??-1}has(t){return this.styleIDMap.has(t)}add(t,r){if(this.has(t))return this.getId(t);let l;return r===void 0?l=this.id++:l=r,this.styleIDMap.set(t,l),this.idStyleMap.set(l,t),l}getStyle(t){return this.idStyleMap.get(t)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function Ji(e){var t;let r=null;return"getRootNode"in e&&((t=L.getRootNode(e))==null?void 0:t.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&L.host(L.getRootNode(e))&&(r=L.host(L.getRootNode(e))),r}function jo(e){let t=e,r;for(;r=Ji(t);)t=r;return t}function Ho(e){const t=e.ownerDocument;if(!t)return!1;const r=jo(e);return L.contains(t,r)}function Yi(e){const t=e.ownerDocument;return t?L.contains(t,e)||Ho(e):!1}var U=(e=>(e[e.DomContentLoaded=0]="DomContentLoaded",e[e.Load=1]="Load",e[e.FullSnapshot=2]="FullSnapshot",e[e.IncrementalSnapshot=3]="IncrementalSnapshot",e[e.Meta=4]="Meta",e[e.Custom=5]="Custom",e[e.Plugin=6]="Plugin",e))(U||{}),D=(e=>(e[e.Mutation=0]="Mutation",e[e.MouseMove=1]="MouseMove",e[e.MouseInteraction=2]="MouseInteraction",e[e.Scroll=3]="Scroll",e[e.ViewportResize=4]="ViewportResize",e[e.Input=5]="Input",e[e.TouchMove=6]="TouchMove",e[e.MediaInteraction=7]="MediaInteraction",e[e.StyleSheetRule=8]="StyleSheetRule",e[e.CanvasMutation=9]="CanvasMutation",e[e.Font=10]="Font",e[e.Log=11]="Log",e[e.Drag=12]="Drag",e[e.StyleDeclaration=13]="StyleDeclaration",e[e.Selection=14]="Selection",e[e.AdoptedStyleSheet=15]="AdoptedStyleSheet",e[e.CustomElement=16]="CustomElement",e))(D||{}),ie=(e=>(e[e.MouseUp=0]="MouseUp",e[e.MouseDown=1]="MouseDown",e[e.Click=2]="Click",e[e.ContextMenu=3]="ContextMenu",e[e.DblClick=4]="DblClick",e[e.Focus=5]="Focus",e[e.Blur=6]="Blur",e[e.TouchStart=7]="TouchStart",e[e.TouchMove_Departed=8]="TouchMove_Departed",e[e.TouchEnd=9]="TouchEnd",e[e.TouchCancel=10]="TouchCancel",e))(ie||{}),ge=(e=>(e[e.Mouse=0]="Mouse",e[e.Pen=1]="Pen",e[e.Touch=2]="Touch",e))(ge||{}),_e=(e=>(e[e["2D"]=0]="2D",e[e.WebGL=1]="WebGL",e[e.WebGL2=2]="WebGL2",e))(_e||{}),Ae=(e=>(e[e.Play=0]="Play",e[e.Pause=1]="Pause",e[e.Seeked=2]="Seeked",e[e.VolumeChange=3]="VolumeChange",e[e.RateChange=4]="RateChange",e))(Ae||{}),Qi=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Qi||{});function oi(e){return"__ln"in e}class Go{constructor(){I(this,"length",0),I(this,"head",null),I(this,"tail",null)}get(t){if(t>=this.length)throw new Error("Position outside of list range");let r=this.head;for(let l=0;l`${e}@${t}`;class Vo{constructor(){I(this,"frozen",!1),I(this,"locked",!1),I(this,"texts",[]),I(this,"attributes",[]),I(this,"attributeMap",new WeakMap),I(this,"removes",[]),I(this,"mapRemoves",[]),I(this,"movedMap",{}),I(this,"addedSet",new Set),I(this,"movedSet",new Set),I(this,"droppedSet",new Set),I(this,"removesSubTreeCache",new Set),I(this,"mutationCb"),I(this,"blockClass"),I(this,"blockSelector"),I(this,"maskTextClass"),I(this,"maskTextSelector"),I(this,"inlineStylesheet"),I(this,"maskInputOptions"),I(this,"maskTextFn"),I(this,"maskInputFn"),I(this,"maskAttributeFn"),I(this,"keepIframeSrcFn"),I(this,"recordCanvas"),I(this,"inlineImages"),I(this,"slimDOMOptions"),I(this,"dataURLOptions"),I(this,"doc"),I(this,"mirror"),I(this,"iframeManager"),I(this,"stylesheetManager"),I(this,"shadowDomManager"),I(this,"canvasManager"),I(this,"processedNodeManager"),I(this,"unattachedDoc"),I(this,"processMutations",t=>{t.forEach(this.processMutation),this.emit()}),I(this,"emit",()=>{if(this.frozen||this.locked)return;const t=[],r=new Set,l=new Go,s=a=>{let p=a,i=$e;for(;i===$e;)p=p&&p.nextSibling,i=p&&this.mirror.getId(p);return i},f=a=>{const p=L.parentNode(a);if(!p||!Yi(a))return;let i=!1;if(a.nodeType===Node.TEXT_NODE){const d=p.tagName;if(d==="TEXTAREA")return;d==="STYLE"&&this.addedSet.has(p)&&(i=!0)}const c=Te(p)?this.mirror.getId(Ji(a)):this.mirror.getId(p),o=s(a);if(c===-1||o===-1)return l.addNode(a);const n=Ne(a,{doc:this.doc,mirror:this.mirror,blockClass:this.blockClass,blockSelector:this.blockSelector,maskTextClass:this.maskTextClass,maskTextSelector:this.maskTextSelector,skipChild:!0,newlyAddedElement:!0,inlineStylesheet:this.inlineStylesheet,maskInputOptions:this.maskInputOptions,maskTextFn:this.maskTextFn,maskInputFn:this.maskInputFn,slimDOMOptions:this.slimDOMOptions,dataURLOptions:this.dataURLOptions,recordCanvas:this.recordCanvas,inlineImages:this.inlineImages,onSerialize:d=>{Gi(d,this.mirror)&&this.iframeManager.addIframe(d),Vi(d,this.mirror)&&this.stylesheetManager.trackLinkElement(d),Ir(a)&&this.shadowDomManager.addShadowRoot(L.shadowRoot(a),this.doc)},onIframeLoad:(d,h)=>{this.iframeManager.attachIframe(d,h),this.shadowDomManager.observeAttachShadow(d)},onStylesheetLoad:(d,h)=>{this.stylesheetManager.attachLinkElement(d,h)},cssCaptured:i});n&&(t.push({parentId:c,nextId:o,node:n}),r.add(n.id))};for(;this.mapRemoves.length;)this.mirror.removeNodeFromMap(this.mapRemoves.shift());for(const a of this.movedSet)li(this.removesSubTreeCache,a,this.mirror)&&!this.movedSet.has(L.parentNode(a))||f(a);for(const a of this.addedSet)!ui(this.droppedSet,a)&&!li(this.removesSubTreeCache,a,this.mirror)||ui(this.movedSet,a)?f(a):this.droppedSet.add(a);let u=null;for(;l.length;){let a=null;if(u){const p=this.mirror.getId(L.parentNode(u.value)),i=s(u.value);p!==-1&&i!==-1&&(a=u)}if(!a){let p=l.tail;for(;p;){const i=p;if(p=p.previous,i){const c=this.mirror.getId(L.parentNode(i.value));if(s(i.value)===-1)continue;if(c!==-1){a=i;break}else{const n=i.value,d=L.parentNode(n);if(d&&d.nodeType===Node.DOCUMENT_FRAGMENT_NODE){const h=L.host(d);if(this.mirror.getId(h)!==-1){a=i;break}}}}}}if(!a){for(;l.head;)l.removeNode(l.head.value);break}u=a.previous,l.removeNode(a.value),f(a.value)}const m={texts:this.texts.map(a=>{const p=a.node,i=L.parentNode(p);return i&&i.tagName==="TEXTAREA"&&this.genTextAreaValueMutation(i),{id:this.mirror.getId(p),value:a.value}}).filter(a=>!r.has(a.id)).filter(a=>this.mirror.has(a.id)),attributes:this.attributes.map(a=>{const{attributes:p}=a;if(typeof p.style=="string"){const i=JSON.stringify(a.styleDiff),c=JSON.stringify(a._unchangedStyles);i.length!r.has(a.id)).filter(a=>this.mirror.has(a.id)),removes:this.removes,adds:t};!m.texts.length&&!m.attributes.length&&!m.removes.length&&!m.adds.length||(this.texts=[],this.attributes=[],this.attributeMap=new WeakMap,this.removes=[],this.addedSet=new Set,this.movedSet=new Set,this.droppedSet=new Set,this.removesSubTreeCache=new Set,this.movedMap={},this.mutationCb(m))}),I(this,"genTextAreaValueMutation",t=>{let r=this.attributeMap.get(t);r||(r={node:t,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(r),this.attributeMap.set(t,r));const l=Array.from(L.childNodes(t),s=>L.textContent(s)||"").join("");r.attributes.value=st({element:t,maskInputOptions:this.maskInputOptions,tagName:t.tagName,type:it(t),value:l,maskInputFn:this.maskInputFn})}),I(this,"processMutation",t=>{if(!Rr(t.target,this.mirror,this.slimDOMOptions))switch(t.type){case"characterData":{const r=L.textContent(t.target);!re(t.target,this.blockClass,this.blockSelector,!1)&&r!==t.oldValue&&this.texts.push({value:Mi(t.target,this.maskTextClass,this.maskTextSelector,!0)&&r?this.maskTextFn?this.maskTextFn(r,ji(t.target)):r.replace(/[\S]/g,"*"):r,node:t.target});break}case"attributes":{const r=t.target;let l=t.attributeName,s=t.target.getAttribute(l);if(l==="value"){const u=it(r);s=st({element:r,maskInputOptions:this.maskInputOptions,tagName:r.tagName,type:u,value:s,maskInputFn:this.maskInputFn})}if(re(t.target,this.blockClass,this.blockSelector,!1)||s===t.oldValue)return;let f=this.attributeMap.get(t.target);if(r.tagName==="IFRAME"&&l==="src"&&!this.keepIframeSrcFn(s))if(!r.contentDocument)l="rr_src";else return;if(f||(f={node:t.target,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(f),this.attributeMap.set(t.target,f)),l==="type"&&r.tagName==="INPUT"&&(t.oldValue||"").toLowerCase()==="password"&&r.setAttribute("data-rr-is-password","true"),!Oi(r.tagName,l)){if(f.attributes[l]=Ri(this.doc,Ce(r.tagName),Ce(l),s),this.maskAttributeFn&&typeof f.attributes[l]=="string"){const u=this.maskAttributeFn(l,f.attributes[l],r);u!==null?f.attributes[l]=u:delete f.attributes[l]}if(l==="style"){if(!this.unattachedDoc)try{this.unattachedDoc=document.implementation.createHTMLDocument()}catch{this.unattachedDoc=this.doc}const u=this.unattachedDoc.createElement("span");t.oldValue&&u.setAttribute("style",t.oldValue);for(const m of Array.from(r.style)){const a=r.style.getPropertyValue(m),p=r.style.getPropertyPriority(m);a!==u.style.getPropertyValue(m)||p!==u.style.getPropertyPriority(m)?p===""?f.styleDiff[m]=a:f.styleDiff[m]=[a,p]:f._unchangedStyles[m]=[a,p]}for(const m of Array.from(u.style))r.style.getPropertyValue(m)===""&&(f.styleDiff[m]=!1)}else l==="open"&&r.tagName==="DIALOG"&&(r.matches("dialog:modal")?f.attributes.rr_open_mode="modal":f.attributes.rr_open_mode="non-modal")}break}case"childList":{if(re(t.target,this.blockClass,this.blockSelector,!0))return;if(t.target.tagName==="TEXTAREA"){this.genTextAreaValueMutation(t.target);return}t.addedNodes.forEach(r=>this.genAdds(r,t.target)),t.removedNodes.forEach(r=>{const l=this.mirror.getId(r),s=Te(t.target)?this.mirror.getId(L.host(t.target)):this.mirror.getId(t.target);re(t.target,this.blockClass,this.blockSelector,!1)||Rr(r,this.mirror,this.slimDOMOptions)||!zo(r,this.mirror)||(this.addedSet.has(r)?(Ar(this.addedSet,r),this.droppedSet.add(r)):this.addedSet.has(t.target)&&l===-1||Hi(t.target,this.mirror)||(this.movedSet.has(r)&&this.movedMap[ai(l,s)]?Ar(this.movedSet,r):(this.removes.push({parentId:s,id:l,isShadow:Te(t.target)&&Fe(t.target)?!0:void 0}),Jo(r,this.removesSubTreeCache))),this.mapRemoves.push(r))});break}}}),I(this,"genAdds",(t,r)=>{if(!this.processedNodeManager.inOtherBuffer(t,this)&&!(this.addedSet.has(t)||this.movedSet.has(t))){if(this.mirror.hasNode(t)){if(Rr(t,this.mirror,this.slimDOMOptions))return;this.movedSet.add(t);let l=null;r&&this.mirror.hasNode(r)&&(l=this.mirror.getId(r)),l&&l!==-1&&(this.movedMap[ai(this.mirror.getId(t),l)]=!0)}else this.addedSet.add(t),this.droppedSet.delete(t);re(t,this.blockClass,this.blockSelector,!1)||(L.childNodes(t).forEach(l=>this.genAdds(l)),Ir(t)&&L.childNodes(L.shadowRoot(t)).forEach(l=>{this.processedNodeManager.add(l,this),this.genAdds(l,t)}))}})}init(t){["mutationCb","blockClass","blockSelector","maskTextClass","maskTextSelector","inlineStylesheet","maskInputOptions","maskTextFn","maskInputFn","maskAttributeFn","keepIframeSrcFn","recordCanvas","inlineImages","slimDOMOptions","dataURLOptions","doc","mirror","iframeManager","stylesheetManager","shadowDomManager","canvasManager","processedNodeManager"].forEach(r=>{this[r]=t[r]})}freeze(){this.frozen=!0,this.canvasManager.freeze()}unfreeze(){this.frozen=!1,this.canvasManager.unfreeze(),this.emit()}isFrozen(){return this.frozen}lock(){this.locked=!0,this.canvasManager.lock()}unlock(){this.locked=!1,this.canvasManager.unlock(),this.emit()}reset(){this.shadowDomManager.reset(),this.canvasManager.reset()}}function Ar(e,t){e.delete(t),L.childNodes(t).forEach(r=>Ar(e,r))}function Jo(e,t){const r=[e];for(;r.length;){const l=r.pop();t.has(l)||(t.add(l),L.childNodes(l).forEach(s=>r.push(s)))}}function li(e,t,r){return e.size===0?!1:Yo(e,t)}function Yo(e,t,r){const l=L.parentNode(t);return l?e.has(l):!1}function ui(e,t){return e.size===0?!1:Xi(e,t)}function Xi(e,t){const r=L.parentNode(t);return r?e.has(r)?!0:Xi(e,r):!1}let Ue;function Qo(e){Ue=e}function Xo(){Ue=void 0}const T=e=>Ue?((...r)=>{try{return e(...r)}catch(l){if(Ue&&Ue(l)===!0)return;throw l}}):e;function ci(e){return(...t)=>{try{return e(...t)}catch(r){try{r._external_=!0}catch{}throw r}}}const ve=[];function qe(e){try{if("composedPath"in e){const t=e.composedPath();if(t.length)return t[0]}else if("path"in e&&e.path.length)return e.path[0]}catch{}return e&&e.target}function Ki(e,t){const r=new Vo;ve.push(r),r.init(e);const l=new(Bi())(T(r.processMutations.bind(r)));return l.observe(t,{attributes:!0,attributeOldValue:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0}),l}function Ko({mousemoveCb:e,sampling:t,doc:r,mirror:l}){if(t.mousemove===!1)return()=>{};const s=typeof t.mousemove=="number"?t.mousemove:50,f=typeof t.mousemoveCallback=="number"?t.mousemoveCallback:500;let u=[],m;const a=Be(T(c=>{const o=Date.now()-m;e(u.map(n=>(n.timeOffset-=o,n)),c),u=[],m=null}),f),p=T(Be(T(c=>{const o=qe(c),{clientX:n,clientY:d}=Er(c)?c.changedTouches[0]:c;m||(m=at()),u.push({x:n,y:d,id:l.getId(o),timeOffset:at()-m}),a(typeof DragEvent<"u"&&c instanceof DragEvent?D.Drag:c instanceof MouseEvent?D.MouseMove:D.TouchMove)}),s,{trailing:!1})),i=[te("mousemove",p,r),te("touchmove",p,r),te("drag",p,r)];return T(()=>{i.forEach(c=>c())})}function Zo({mouseInteractionCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){if(f.mouseInteraction===!1)return()=>{};const u=f.mouseInteraction===!0||f.mouseInteraction===void 0?{}:f.mouseInteraction,m=[];let a=null;const p=i=>c=>{const o=qe(c);if(re(o,l,s,!0))return;let n=null,d=i;if("pointerType"in c){switch(c.pointerType){case"mouse":n=ge.Mouse;break;case"touch":n=ge.Touch;break;case"pen":n=ge.Pen;break}n===ge.Touch?ie[i]===ie.MouseDown?d="TouchStart":ie[i]===ie.MouseUp&&(d="TouchEnd"):ge.Pen}else Er(c)&&(n=ge.Touch);n!==null?(a=n,(d.startsWith("Touch")&&n===ge.Touch||d.startsWith("Mouse")&&n===ge.Mouse)&&(n=null)):ie[i]===ie.Click&&(n=a,a=null);const h=Er(c)?c.changedTouches[0]:c;if(!h)return;const y=r.getId(o),{clientX:C,clientY:w}=h;T(e)({type:ie[d],id:y,x:C,y:w,...n!==null&&{pointerType:n}})};return Object.keys(ie).filter(i=>Number.isNaN(Number(i))&&!i.endsWith("_Departed")&&u[i]!==!1).forEach(i=>{let c=Ce(i);const o=p(i);if(window.PointerEvent)switch(ie[i]){case ie.MouseDown:case ie.MouseUp:c=c.replace("mouse","pointer");break;case ie.TouchStart:case ie.TouchEnd:return}m.push(te(c,o,t))}),T(()=>{m.forEach(i=>i())})}function Zi({scrollCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){const u=T(Be(T(m=>{const a=qe(m);if(!a||re(a,l,s,!0))return;const p=r.getId(a);if(a===t&&t.defaultView){const i=zi(t.defaultView);e({id:p,x:i.left,y:i.top})}else e({id:p,x:a.scrollLeft,y:a.scrollTop})}),f.scroll||100));return te("scroll",u,t)}function ea({viewportResizeCb:e},{win:t}){let r=-1,l=-1;const s=T(Be(T(()=>{const f=Wi(),u=qi();(r!==f||l!==u)&&(e({width:Number(u),height:Number(f)}),r=f,l=u)}),200));return te("resize",s,t)}const ta=["INPUT","TEXTAREA","SELECT"],hi=new WeakMap;function ra({inputCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,ignoreClass:f,ignoreSelector:u,maskInputOptions:m,maskInputFn:a,sampling:p,userTriggeredOnInput:i}){function c(w){let S=qe(w);const b=w.isTrusted,g=S&&S.tagName;if(S&&g==="OPTION"&&(S=L.parentElement(S)),!S||!g||ta.indexOf(g)<0||re(S,l,s,!0)||S.classList.contains(f)||u&&S.matches(u))return;let v=S.value,x=!1;const O=it(S)||"";O==="radio"||O==="checkbox"?x=S.checked:(m[g.toLowerCase()]||m[O])&&(v=st({element:S,maskInputOptions:m,tagName:g,type:O,value:v,maskInputFn:a})),o(S,i?{text:v,isChecked:x,userTriggered:b}:{text:v,isChecked:x});const A=S.name;O==="radio"&&A&&x&&t.querySelectorAll(`input[type="radio"][name="${A}"]`).forEach(M=>{if(M!==S){const $=M.value;o(M,i?{text:$,isChecked:!x,userTriggered:!1}:{text:$,isChecked:!x})}})}function o(w,S){const b=hi.get(w);if(!b||b.text!==S.text||b.isChecked!==S.isChecked){hi.set(w,S);const g=r.getId(w);T(e)({...S,id:g})}}const d=(p.input==="last"?["change"]:["input","change"]).map(w=>te(w,T(c),t)),h=t.defaultView;if(!h)return()=>{d.forEach(w=>w())};const y=h.Object.getOwnPropertyDescriptor(h.HTMLInputElement.prototype,"value"),C=[[h.HTMLInputElement.prototype,"value"],[h.HTMLInputElement.prototype,"checked"],[h.HTMLSelectElement.prototype,"value"],[h.HTMLTextAreaElement.prototype,"value"],[h.HTMLSelectElement.prototype,"selectedIndex"],[h.HTMLOptionElement.prototype,"selected"]];return y&&y.set&&d.push(...C.map(w=>St(w[0],w[1],{set(){T(c)({target:this,isTrusted:!1})}},!1,h))),T(()=>{d.forEach(w=>w())})}function lt(e){const t=[];function r(l,s){if(Xe("CSSGroupingRule")&&l.parentRule instanceof CSSGroupingRule||Xe("CSSMediaRule")&&l.parentRule instanceof CSSMediaRule||Xe("CSSSupportsRule")&&l.parentRule instanceof CSSSupportsRule||Xe("CSSConditionRule")&&l.parentRule instanceof CSSConditionRule){const u=Array.from(l.parentRule.cssRules).indexOf(l);s.unshift(u)}else if(l.parentStyleSheet){const u=Array.from(l.parentStyleSheet.cssRules).indexOf(l);s.unshift(u)}return s}return r(e,t)}function we(e,t,r){let l,s;return e?(e.ownerNode?l=t.getId(e.ownerNode):s=r.getId(e),{styleId:s,id:l}):{}}function sa({styleSheetRuleCb:e,mirror:t,stylesheetManager:r},{win:l}){if(!l.CSSStyleSheet||!l.CSSStyleSheet.prototype)return()=>{};const s=l.CSSStyleSheet.prototype.insertRule;l.CSSStyleSheet.prototype.insertRule=new Proxy(s,{apply:T((i,c,o)=>{const[n,d]=o,{id:h,styleId:y}=we(c,t,r.styleMirror);return(h&&h!==-1||y&&y!==-1)&&e({id:h,styleId:y,adds:[{rule:n,index:d}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.addRule=function(i,c,o=this.cssRules.length){const n=`${i} { ${c} }`;return l.CSSStyleSheet.prototype.insertRule.apply(this,[n,o])};const f=l.CSSStyleSheet.prototype.deleteRule;l.CSSStyleSheet.prototype.deleteRule=new Proxy(f,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,removes:[{index:n}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.removeRule=function(i){return l.CSSStyleSheet.prototype.deleteRule.apply(this,[i])};let u;l.CSSStyleSheet.prototype.replace&&(u=l.CSSStyleSheet.prototype.replace,l.CSSStyleSheet.prototype.replace=new Proxy(u,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replace:n}),i.apply(c,o)})}));let m;l.CSSStyleSheet.prototype.replaceSync&&(m=l.CSSStyleSheet.prototype.replaceSync,l.CSSStyleSheet.prototype.replaceSync=new Proxy(m,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replaceSync:n}),i.apply(c,o)})}));const a={};Ke("CSSGroupingRule")?a.CSSGroupingRule=l.CSSGroupingRule:(Ke("CSSMediaRule")&&(a.CSSMediaRule=l.CSSMediaRule),Ke("CSSConditionRule")&&(a.CSSConditionRule=l.CSSConditionRule),Ke("CSSSupportsRule")&&(a.CSSSupportsRule=l.CSSSupportsRule));const p={};return Object.entries(a).forEach(([i,c])=>{p[i]={insertRule:c.prototype.insertRule,deleteRule:c.prototype.deleteRule},c.prototype.insertRule=new Proxy(p[i].insertRule,{apply:T((o,n,d)=>{const[h,y]=d,{id:C,styleId:w}=we(n.parentStyleSheet,t,r.styleMirror);return(C&&C!==-1||w&&w!==-1)&&e({id:C,styleId:w,adds:[{rule:h,index:[...lt(n),y||0]}]}),o.apply(n,d)})}),c.prototype.deleteRule=new Proxy(p[i].deleteRule,{apply:T((o,n,d)=>{const[h]=d,{id:y,styleId:C}=we(n.parentStyleSheet,t,r.styleMirror);return(y&&y!==-1||C&&C!==-1)&&e({id:y,styleId:C,removes:[{index:[...lt(n),h]}]}),o.apply(n,d)})})}),T(()=>{l.CSSStyleSheet.prototype.insertRule=s,l.CSSStyleSheet.prototype.deleteRule=f,u&&(l.CSSStyleSheet.prototype.replace=u),m&&(l.CSSStyleSheet.prototype.replaceSync=m),Object.entries(a).forEach(([i,c])=>{c.prototype.insertRule=p[i].insertRule,c.prototype.deleteRule=p[i].deleteRule})})}function en({mirror:e,stylesheetManager:t},r){var l,s,f;let u=null;r.nodeName==="#document"?u=e.getId(r):u=e.getId(L.host(r));const m=r.nodeName==="#document"?(l=r.defaultView)==null?void 0:l.Document:(f=(s=r.ownerDocument)==null?void 0:s.defaultView)==null?void 0:f.ShadowRoot,a=m?.prototype?Object.getOwnPropertyDescriptor(m?.prototype,"adoptedStyleSheets"):void 0;return u===null||u===-1||!m||!a?()=>{}:(Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get(){var p;return(p=a.get)==null?void 0:p.call(this)},set(p){var i;const c=(i=a.set)==null?void 0:i.call(this,p);if(u!==null&&u!==-1)try{t.adoptStyleSheets(p,u)}catch{}return c}}),T(()=>{Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get:a.get,set:a.set})}))}function ia({styleDeclarationCb:e,mirror:t,ignoreCSSAttributes:r,stylesheetManager:l},{win:s}){const f=s.CSSStyleDeclaration.prototype.setProperty;s.CSSStyleDeclaration.prototype.setProperty=new Proxy(f,{apply:T((m,a,p)=>{var i;const[c,o,n]=p;if(r.has(c))return f.apply(a,[c,o,n]);const{id:d,styleId:h}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,set:{property:c,value:o,priority:n},index:lt(a.parentRule)}),m.apply(a,p)})});const u=s.CSSStyleDeclaration.prototype.removeProperty;return s.CSSStyleDeclaration.prototype.removeProperty=new Proxy(u,{apply:T((m,a,p)=>{var i;const[c]=p;if(r.has(c))return u.apply(a,[c]);const{id:o,styleId:n}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(o&&o!==-1||n&&n!==-1)&&e({id:o,styleId:n,remove:{property:c},index:lt(a.parentRule)}),m.apply(a,p)})}),T(()=>{s.CSSStyleDeclaration.prototype.setProperty=f,s.CSSStyleDeclaration.prototype.removeProperty=u})}function na({mediaInteractionCb:e,blockClass:t,blockSelector:r,mirror:l,sampling:s,doc:f}){const u=T(a=>Be(T(p=>{const i=qe(p);if(!i||re(i,t,r,!0))return;const{currentTime:c,volume:o,muted:n,playbackRate:d,loop:h}=i;e({type:a,id:l.getId(i),currentTime:c,volume:o,muted:n,playbackRate:d,loop:h})}),s.media||500)),m=[te("play",u(Ae.Play),f),te("pause",u(Ae.Pause),f),te("seeked",u(Ae.Seeked),f),te("volumechange",u(Ae.VolumeChange),f),te("ratechange",u(Ae.RateChange),f)];return T(()=>{m.forEach(a=>a())})}function oa({fontCb:e,doc:t}){const r=t.defaultView;if(!r)return()=>{};const l=[],s=new WeakMap,f=r.FontFace;r.FontFace=function(a,p,i){const c=new f(a,p,i);return s.set(c,{family:a,buffer:typeof p!="string",descriptors:i,fontSource:typeof p=="string"?p:JSON.stringify(Array.from(new Uint8Array(p)))}),c};const u=Le(t.fonts,"add",function(m){return function(a){return setTimeout(T(()=>{const p=s.get(a);p&&(e(p),s.delete(a))}),0),m.apply(this,[a])}});return l.push(()=>{r.FontFace=f}),l.push(u),T(()=>{l.forEach(m=>m())})}function aa(e){const{doc:t,mirror:r,blockClass:l,blockSelector:s,selectionCb:f}=e;let u=!0;const m=T(()=>{const a=t.getSelection();if(!a||u&&a?.isCollapsed)return;u=a.isCollapsed||!1;const p=[],i=a.rangeCount||0;for(let c=0;c{}:Le(r.customElements,"define",function(s){return function(f,u,m){try{t({define:{name:f}})}catch{console.warn(`Custom element callback failed for ${f}`)}return s.apply(this,[f,u,m])}})}function ua(e,t){const{mutationCb:r,mousemoveCb:l,mouseInteractionCb:s,scrollCb:f,viewportResizeCb:u,inputCb:m,mediaInteractionCb:a,styleSheetRuleCb:p,styleDeclarationCb:i,canvasMutationCb:c,fontCb:o,selectionCb:n,customElementCb:d}=e;e.mutationCb=(...h)=>{t.mutation&&t.mutation(...h),r(...h)},e.mousemoveCb=(...h)=>{t.mousemove&&t.mousemove(...h),l(...h)},e.mouseInteractionCb=(...h)=>{t.mouseInteraction&&t.mouseInteraction(...h),s(...h)},e.scrollCb=(...h)=>{t.scroll&&t.scroll(...h),f(...h)},e.viewportResizeCb=(...h)=>{t.viewportResize&&t.viewportResize(...h),u(...h)},e.inputCb=(...h)=>{t.input&&t.input(...h),m(...h)},e.mediaInteractionCb=(...h)=>{t.mediaInteaction&&t.mediaInteaction(...h),a(...h)},e.styleSheetRuleCb=(...h)=>{t.styleSheetRule&&t.styleSheetRule(...h),p(...h)},e.styleDeclarationCb=(...h)=>{t.styleDeclaration&&t.styleDeclaration(...h),i(...h)},e.canvasMutationCb=(...h)=>{t.canvasMutation&&t.canvasMutation(...h),c(...h)},e.fontCb=(...h)=>{t.font&&t.font(...h),o(...h)},e.selectionCb=(...h)=>{t.selection&&t.selection(...h),n(...h)},e.customElementCb=(...h)=>{t.customElement&&t.customElement(...h),d(...h)}}function ca(e,t={}){const r=e.doc.defaultView;if(!r)return()=>{};ua(e,t);let l;e.recordDOM&&(l=Ki(e,e.doc));const s=Ko(e),f=Zo(e),u=Zi(e),m=ea(e,{win:r}),a=ra(e),p=na(e);let i=()=>{},c=()=>{},o=()=>{},n=()=>{};e.recordDOM&&(i=sa(e,{win:r}),c=en(e,e.doc),o=ia(e,{win:r}),e.collectFonts&&(n=oa(e)));const d=aa(e),h=la(e),y=[];for(const C of e.plugins)y.push(C.observer(C.callback,r,C.options));return T(()=>{ve.forEach(C=>C.reset()),l?.disconnect(),s(),f(),u(),m(),a(),p(),i(),c(),o(),n(),d(),h(),y.forEach(C=>C())})}function Xe(e){return typeof window[e]<"u"}function Ke(e){return!!(typeof window[e]<"u"&&window[e].prototype&&"insertRule"in window[e].prototype&&"deleteRule"in window[e].prototype)}class fi{constructor(t){I(this,"iframeIdToRemoteIdMap",new WeakMap),I(this,"iframeRemoteIdToIdMap",new WeakMap),this.generateIdFn=t}getId(t,r,l,s){const f=l||this.getIdToRemoteIdMap(t),u=s||this.getRemoteIdToIdMap(t);let m=f.get(r);return m||(m=this.generateIdFn(),f.set(r,m),u.set(m,r)),m}getIds(t,r){const l=this.getIdToRemoteIdMap(t),s=this.getRemoteIdToIdMap(t);return r.map(f=>this.getId(t,f,l,s))}getRemoteId(t,r,l){const s=l||this.getRemoteIdToIdMap(t);if(typeof r!="number")return r;const f=s.get(r);return f||-1}getRemoteIds(t,r){const l=this.getRemoteIdToIdMap(t);return r.map(s=>this.getRemoteId(t,s,l))}reset(t){if(!t){this.iframeIdToRemoteIdMap=new WeakMap,this.iframeRemoteIdToIdMap=new WeakMap;return}this.iframeIdToRemoteIdMap.delete(t),this.iframeRemoteIdToIdMap.delete(t)}getIdToRemoteIdMap(t){let r=this.iframeIdToRemoteIdMap.get(t);return r||(r=new Map,this.iframeIdToRemoteIdMap.set(t,r)),r}getRemoteIdToIdMap(t){let r=this.iframeRemoteIdToIdMap.get(t);return r||(r=new Map,this.iframeRemoteIdToIdMap.set(t,r)),r}}class ha{constructor(t){I(this,"iframes",new WeakMap),I(this,"crossOriginIframeMap",new WeakMap),I(this,"crossOriginIframeMirror",new fi(xi)),I(this,"crossOriginIframeStyleMirror"),I(this,"crossOriginIframeRootIdMap",new WeakMap),I(this,"mirror"),I(this,"mutationCb"),I(this,"wrappedEmit"),I(this,"loadListener"),I(this,"stylesheetManager"),I(this,"recordCrossOriginIframes"),this.mutationCb=t.mutationCb,this.wrappedEmit=t.wrappedEmit,this.stylesheetManager=t.stylesheetManager,this.recordCrossOriginIframes=t.recordCrossOriginIframes,this.crossOriginIframeStyleMirror=new fi(this.stylesheetManager.styleMirror.generateId.bind(this.stylesheetManager.styleMirror)),this.mirror=t.mirror,this.recordCrossOriginIframes&&window.addEventListener("message",this.handleMessage.bind(this))}addIframe(t){this.iframes.set(t,!0),t.contentWindow&&this.crossOriginIframeMap.set(t.contentWindow,t)}addLoadListener(t){this.loadListener=t}attachIframe(t,r){var l,s;this.mutationCb({adds:[{parentId:this.mirror.getId(t),nextId:null,node:r}],removes:[],texts:[],attributes:[],isAttachIframe:!0}),this.recordCrossOriginIframes&&((l=t.contentWindow)==null||l.addEventListener("message",this.handleMessage.bind(this))),(s=this.loadListener)==null||s.call(this,t),t.contentDocument&&t.contentDocument.adoptedStyleSheets&&t.contentDocument.adoptedStyleSheets.length>0&&this.stylesheetManager.adoptStyleSheets(t.contentDocument.adoptedStyleSheets,this.mirror.getId(t.contentDocument))}handleMessage(t){const r=t;if(r.data.type!=="rrweb"||r.origin!==r.data.origin||!t.source)return;const s=this.crossOriginIframeMap.get(t.source);if(!s)return;const f=this.transformCrossOriginEvent(s,r.data.event);f&&this.wrappedEmit(f,r.data.isCheckout)}transformCrossOriginEvent(t,r){var l;switch(r.type){case U.FullSnapshot:{this.crossOriginIframeMirror.reset(t),this.crossOriginIframeStyleMirror.reset(t),this.replaceIdOnNode(r.data.node,t);const s=r.data.node.id;return this.crossOriginIframeRootIdMap.set(t,s),this.patchRootIdOnNode(r.data.node,s),{timestamp:r.timestamp,type:U.IncrementalSnapshot,data:{source:D.Mutation,adds:[{parentId:this.mirror.getId(t),nextId:null,node:r.data.node}],removes:[],texts:[],attributes:[],isAttachIframe:!0}}}case U.Meta:case U.Load:case U.DomContentLoaded:return!1;case U.Plugin:return r;case U.Custom:return this.replaceIds(r.data.payload,t,["id","parentId","previousId","nextId"]),r;case U.IncrementalSnapshot:switch(r.data.source){case D.Mutation:return r.data.adds.forEach(s=>{this.replaceIds(s,t,["parentId","nextId","previousId"]),this.replaceIdOnNode(s.node,t);const f=this.crossOriginIframeRootIdMap.get(t);f&&this.patchRootIdOnNode(s.node,f)}),r.data.removes.forEach(s=>{this.replaceIds(s,t,["parentId","id"])}),r.data.attributes.forEach(s=>{this.replaceIds(s,t,["id"])}),r.data.texts.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.Drag:case D.TouchMove:case D.MouseMove:return r.data.positions.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.ViewportResize:return!1;case D.MediaInteraction:case D.MouseInteraction:case D.Scroll:case D.CanvasMutation:case D.Input:return this.replaceIds(r.data,t,["id"]),r;case D.StyleSheetRule:case D.StyleDeclaration:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleId"]),r;case D.Font:return r;case D.Selection:return r.data.ranges.forEach(s=>{this.replaceIds(s,t,["start","end"])}),r;case D.AdoptedStyleSheet:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleIds"]),(l=r.data.styles)==null||l.forEach(s=>{this.replaceStyleIds(s,t,["styleId"])}),r}}return!1}replace(t,r,l,s){for(const f of s)!Array.isArray(r[f])&&typeof r[f]!="number"||(Array.isArray(r[f])?r[f]=t.getIds(l,r[f]):r[f]=t.getId(l,r[f]));return r}replaceIds(t,r,l){return this.replace(this.crossOriginIframeMirror,t,r,l)}replaceStyleIds(t,r,l){return this.replace(this.crossOriginIframeStyleMirror,t,r,l)}replaceIdOnNode(t,r){this.replaceIds(t,r,["id","rootId"]),"childNodes"in t&&t.childNodes.forEach(l=>{this.replaceIdOnNode(l,r)})}patchRootIdOnNode(t,r){t.type!==Qi.Document&&!t.rootId&&(t.rootId=r),"childNodes"in t&&t.childNodes.forEach(l=>{this.patchRootIdOnNode(l,r)})}}class fa{constructor(t){I(this,"shadowDoms",new WeakSet),I(this,"mutationCb"),I(this,"scrollCb"),I(this,"bypassOptions"),I(this,"mirror"),I(this,"restoreHandlers",[]),this.mutationCb=t.mutationCb,this.scrollCb=t.scrollCb,this.bypassOptions=t.bypassOptions,this.mirror=t.mirror,this.init()}init(){this.reset(),this.patchAttachShadow(Element,document)}addShadowRoot(t,r){if(!Fe(t)||this.shadowDoms.has(t))return;this.shadowDoms.add(t);const l=Ki({...this.bypassOptions,doc:r,mutationCb:this.mutationCb,mirror:this.mirror,shadowDomManager:this},t);this.restoreHandlers.push(()=>l.disconnect()),this.restoreHandlers.push(Zi({...this.bypassOptions,scrollCb:this.scrollCb,doc:t,mirror:this.mirror})),setTimeout(()=>{t.adoptedStyleSheets&&t.adoptedStyleSheets.length>0&&this.bypassOptions.stylesheetManager.adoptStyleSheets(t.adoptedStyleSheets,this.mirror.getId(L.host(t))),this.restoreHandlers.push(en({mirror:this.mirror,stylesheetManager:this.bypassOptions.stylesheetManager},t))},0)}observeAttachShadow(t){!t.contentWindow||!t.contentDocument||this.patchAttachShadow(t.contentWindow.Element,t.contentDocument)}patchAttachShadow(t,r){const l=this;this.restoreHandlers.push(Le(t.prototype,"attachShadow",function(s){return function(f){const u=s.call(this,f),m=L.shadowRoot(this);return m&&Yi(this)&&l.addShadowRoot(m,r),u}}))}reset(){this.restoreHandlers.forEach(t=>{try{t()}catch{}}),this.restoreHandlers=[],this.shadowDoms=new WeakSet}}var Pe="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",pa=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var Ze=0;Ze>2],s+=Pe[(t[r]&3)<<4|t[r+1]>>4],s+=Pe[(t[r+1]&15)<<2|t[r+2]>>6],s+=Pe[t[r+2]&63];return l%3===2?s=s.substring(0,s.length-1)+"=":l%3===1&&(s=s.substring(0,s.length-2)+"=="),s};const pi=new Map;function ma(e,t){let r=pi.get(e);return r||(r=new Map,pi.set(e,r)),r.has(t)||r.set(t,[]),r.get(t)}const tn=(e,t,r)=>{if(!e||!(sn(e,t)||typeof e=="object"))return;const l=e.constructor.name,s=ma(r,l);let f=s.indexOf(e);return f===-1&&(f=s.length,s.push(e)),f};function et(e,t,r){if(e instanceof Array)return e.map(l=>et(l,t,r));if(e===null)return e;if(e instanceof Float32Array||e instanceof Float64Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Uint8Array||e instanceof Uint16Array||e instanceof Int16Array||e instanceof Int8Array||e instanceof Uint8ClampedArray)return{rr_type:e.constructor.name,args:[Object.values(e)]};if(e instanceof ArrayBuffer){const l=e.constructor.name,s=da(e);return{rr_type:l,base64:s}}else{if(e instanceof DataView)return{rr_type:e.constructor.name,args:[et(e.buffer,t,r),e.byteOffset,e.byteLength]};if(e instanceof HTMLImageElement){const l=e.constructor.name,{src:s}=e;return{rr_type:l,src:s}}else if(e instanceof HTMLCanvasElement){const l="HTMLImageElement",s=e.toDataURL();return{rr_type:l,src:s}}else{if(e instanceof ImageData)return{rr_type:e.constructor.name,args:[et(e.data,t,r),e.width,e.height]};if(sn(e,t)||typeof e=="object"){const l=e.constructor.name,s=tn(e,t,r);return{rr_type:l,index:s}}}}return e}const rn=(e,t,r)=>e.map(l=>et(l,t,r)),sn=(e,t)=>!!["WebGLActiveInfo","WebGLBuffer","WebGLFramebuffer","WebGLProgram","WebGLRenderbuffer","WebGLShader","WebGLShaderPrecisionFormat","WebGLTexture","WebGLUniformLocation","WebGLVertexArrayObject","WebGLVertexArrayObjectOES"].filter(s=>typeof t[s]=="function").find(s=>e instanceof t[s]);function ga(e,t,r,l){const s=[],f=Object.getOwnPropertyNames(t.CanvasRenderingContext2D.prototype);for(const u of f)try{if(typeof t.CanvasRenderingContext2D.prototype[u]!="function")continue;const m=Le(t.CanvasRenderingContext2D.prototype,u,function(a){return function(...p){return re(this.canvas,r,l,!0)||setTimeout(()=>{const i=rn(p,t,this);e(this.canvas,{type:_e["2D"],property:u,args:i})},0),a.apply(this,p)}});s.push(m)}catch{const m=St(t.CanvasRenderingContext2D.prototype,u,{set(a){e(this.canvas,{type:_e["2D"],property:u,args:[a],setter:!0})}});s.push(m)}return()=>{s.forEach(u=>u())}}function ya(e){return e==="experimental-webgl"?"webgl":e}function di(e,t,r,l){const s=[];try{const f=Le(e.HTMLCanvasElement.prototype,"getContext",function(u){return function(m,...a){if(!re(this,t,r,!0)){const p=ya(m);if("__context"in this||(this.__context=p),l&&["webgl","webgl2"].includes(p))if(a[0]&&typeof a[0]=="object"){const i=a[0];i.preserveDrawingBuffer||(i.preserveDrawingBuffer=!0)}else a.splice(0,1,{preserveDrawingBuffer:!0})}return u.apply(this,[m,...a])}});s.push(f)}catch{console.error("failed to patch HTMLCanvasElement.prototype.getContext")}return()=>{s.forEach(f=>f())}}function mi(e,t,r,l,s,f){const u=[],m=Object.getOwnPropertyNames(e);for(const a of m)if(!["isContextLost","canvas","drawingBufferWidth","drawingBufferHeight"].includes(a))try{if(typeof e[a]!="function")continue;const p=Le(e,a,function(i){return function(...c){const o=i.apply(this,c);if(tn(o,f,this),"tagName"in this.canvas&&!re(this.canvas,l,s,!0)){const n=rn(c,f,this),d={type:t,property:a,args:n};r(this.canvas,d)}return o}});u.push(p)}catch{const p=St(e,a,{set(i){r(this.canvas,{type:t,property:a,args:[i],setter:!0})}});u.push(p)}return u}function wa(e,t,r,l){const s=[];return s.push(...mi(t.WebGLRenderingContext.prototype,_e.WebGL,e,r,l,t)),typeof t.WebGL2RenderingContext<"u"&&s.push(...mi(t.WebGL2RenderingContext.prototype,_e.WebGL2,e,r,l,t)),()=>{s.forEach(f=>f())}}const nn=`(function() { - "use strict"; - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256); - for (var i = 0; i < chars.length; i++) { - lookup[chars.charCodeAt(i)] = i; - } - var encode = function(arraybuffer) { - var bytes = new Uint8Array(arraybuffer), i2, len = bytes.length, base64 = ""; - for (i2 = 0; i2 < len; i2 += 3) { - base64 += chars[bytes[i2] >> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`,gi=typeof self<"u"&&self.Blob&&new Blob([nn],{type:"text/javascript;charset=utf-8"});function ba(e){let t;try{if(t=gi&&(self.URL||self.webkitURL).createObjectURL(gi),!t)throw"";const r=new Worker(t,{name:e?.name});return r.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(t)}),r}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(nn),{name:e?.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}class Sa{constructor(t){I(this,"pendingCanvasMutations",new Map),I(this,"rafStamps",{latestId:0,invokeId:null}),I(this,"mirror"),I(this,"mutationCb"),I(this,"resetObservers"),I(this,"frozen",!1),I(this,"locked",!1),I(this,"processMutation",(a,p)=>{(this.rafStamps.invokeId&&this.rafStamps.latestId!==this.rafStamps.invokeId||!this.rafStamps.invokeId)&&(this.rafStamps.invokeId=this.rafStamps.latestId),this.pendingCanvasMutations.has(a)||this.pendingCanvasMutations.set(a,[]),this.pendingCanvasMutations.get(a).push(p)});const{sampling:r="all",win:l,blockClass:s,blockSelector:f,recordCanvas:u,dataURLOptions:m}=t;this.mutationCb=t.mutationCb,this.mirror=t.mirror,u&&r==="all"&&this.initCanvasMutationObserver(l,s,f),u&&typeof r=="number"&&this.initCanvasFPSObserver(r,l,s,f,{dataURLOptions:m})}reset(){this.pendingCanvasMutations.clear(),this.resetObservers&&this.resetObservers()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1}lock(){this.locked=!0}unlock(){this.locked=!1}initCanvasFPSObserver(t,r,l,s,f){const u=di(r,l,s,!0),m=new Map,a=new ba;a.onmessage=d=>{const{id:h}=d.data;if(m.set(h,!1),!("base64"in d.data))return;const{base64:y,type:C,width:w,height:S}=d.data;this.mutationCb({id:h,type:_e["2D"],commands:[{property:"clearRect",args:[0,0,w,S]},{property:"drawImage",args:[{rr_type:"ImageBitmap",args:[{rr_type:"Blob",data:[{rr_type:"ArrayBuffer",base64:y}],type:C}]},0,0]}]})};const p=1e3/t;let i=0,c;const o=()=>{const d=[];return r.document.querySelectorAll("canvas").forEach(h=>{re(h,l,s,!0)||d.push(h)}),d},n=d=>{if(i&&d-i{var y;const C=this.mirror.getId(h);if(m.get(C)||h.width===0||h.height===0)return;if(m.set(C,!0),["webgl","webgl2"].includes(h.__context)){const S=h.getContext(h.__context);((y=S?.getContextAttributes())==null?void 0:y.preserveDrawingBuffer)===!1&&S.clear(S.COLOR_BUFFER_BIT)}const w=await createImageBitmap(h);a.postMessage({id:C,bitmap:w,width:h.width,height:h.height,dataURLOptions:f.dataURLOptions},[w])}),c=requestAnimationFrame(n)};c=requestAnimationFrame(n),this.resetObservers=()=>{u(),cancelAnimationFrame(c)}}initCanvasMutationObserver(t,r,l){this.startRAFTimestamping(),this.startPendingCanvasMutationFlusher();const s=di(t,r,l,!1),f=ga(this.processMutation.bind(this),t,r,l),u=wa(this.processMutation.bind(this),t,r,l);this.resetObservers=()=>{s(),f(),u()}}startPendingCanvasMutationFlusher(){requestAnimationFrame(()=>this.flushPendingCanvasMutations())}startRAFTimestamping(){const t=r=>{this.rafStamps.latestId=r,requestAnimationFrame(t)};requestAnimationFrame(t)}flushPendingCanvasMutations(){this.pendingCanvasMutations.forEach((t,r)=>{const l=this.mirror.getId(r);this.flushPendingCanvasMutationFor(r,l)}),requestAnimationFrame(()=>this.flushPendingCanvasMutations())}flushPendingCanvasMutationFor(t,r){if(this.frozen||this.locked)return;const l=this.pendingCanvasMutations.get(t);if(!l||r===-1)return;const s=l.map(u=>{const{type:m,...a}=u;return a}),{type:f}=l[0];this.mutationCb({id:r,type:f,commands:s}),this.pendingCanvasMutations.delete(t)}}class va{constructor(t){I(this,"trackedLinkElements",new WeakSet),I(this,"mutationCb"),I(this,"adoptedStyleSheetCb"),I(this,"styleMirror",new qo),this.mutationCb=t.mutationCb,this.adoptedStyleSheetCb=t.adoptedStyleSheetCb}attachLinkElement(t,r){"_cssText"in r.attributes&&this.mutationCb({adds:[],removes:[],texts:[],attributes:[{id:r.id,attributes:r.attributes}]}),this.trackLinkElement(t)}trackLinkElement(t){this.trackedLinkElements.has(t)||(this.trackedLinkElements.add(t),this.trackStylesheetInLinkElement(t))}adoptStyleSheets(t,r){if(t.length===0)return;const l={id:r,styleIds:[]},s=[];for(const f of t){let u;this.styleMirror.has(f)?u=this.styleMirror.getId(f):(u=this.styleMirror.add(f),s.push({styleId:u,rules:Array.from(f.rules||CSSRule,(m,a)=>({rule:Si(m,f.href),index:a}))})),l.styleIds.push(u)}s.length>0&&(l.styles=s),this.adoptedStyleSheetCb(l)}reset(){this.styleMirror.reset(),this.trackedLinkElements=new WeakSet}trackStylesheetInLinkElement(t){}}class Ca{constructor(){I(this,"nodeMap",new WeakMap),I(this,"active",!1)}inOtherBuffer(t,r){const l=this.nodeMap.get(t);return l&&Array.from(l).some(s=>s!==r)}add(t,r){this.active||(this.active=!0,requestAnimationFrame(()=>{this.nodeMap=new WeakMap,this.active=!1})),this.nodeMap.set(t,(this.nodeMap.get(t)||new Set).add(r))}destroy(){}}let J,tt,Or,ut=!1;try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((Jr=e.contentWindow)==null?void 0:Jr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}const ce=In();function Oe(e={}){const{emit:t,checkoutEveryNms:r,checkoutEveryNth:l,blockClass:s="rr-block",blockSelector:f=null,ignoreClass:u="rr-ignore",ignoreSelector:m=null,maskTextClass:a="rr-mask",maskTextSelector:p=null,inlineStylesheet:i=!0,maskAllInputs:c,maskInputOptions:o,slimDOMOptions:n,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,hooks:C,packFn:w,sampling:S={},dataURLOptions:b={},mousemoveWait:g,recordDOM:v=!0,recordCanvas:x=!1,recordCrossOriginIframes:O=!1,recordAfter:A=e.recordAfter==="DOMContentLoaded"?e.recordAfter:"load",userTriggeredOnInput:M=!1,collectFonts:$=!1,inlineImages:k=!1,plugins:R,keepIframeSrcFn:Z=()=>!1,ignoreCSSAttributes:N=new Set([]),errorHandler:ee,applyBackgroundColorToBlockedElements:H=!1}=e;Qo(ee);const F=O?window.parent===window:!0;let Q=!1;if(!F)try{window.parent.document&&(Q=!1)}catch{Q=!0}if(F&&!t)throw new Error("emit function is required");if(!F&&!Q)return()=>{};g!==void 0&&S.mousemove===void 0&&(S.mousemove=g),ce.reset();const W=c===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:o!==void 0?o:{password:!0},P=n===!0||n==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaVerification:!0,headMetaAuthorship:n==="all",headMetaDescKeywords:n==="all",headTitleMutations:n==="all"}:n||{};Wo();let ye,E=0;const pe=_=>{for(const ue of R||[])ue.eventProcessor&&(_=ue.eventProcessor(_));return w&&!Q&&(_=w(_)),_};J=(_,ue)=>{var G;const V=_;if(V.timestamp=at(),(G=ve[0])!=null&&G.isFrozen()&&V.type!==U.FullSnapshot&&!(V.type===U.IncrementalSnapshot&&V.data.source===D.Mutation)&&ve.forEach(me=>me.unfreeze()),F)t?.(pe(V),ue);else if(Q){const me={type:"rrweb",event:pe(V),origin:window.location.origin,isCheckout:ue};window.parent.postMessage(me,"*")}if(V.type===U.FullSnapshot)ye=V,E=0;else if(V.type===U.IncrementalSnapshot){if(V.data.source===D.Mutation&&V.data.isAttachIframe)return;E++;const me=l&&E>=l,B=r&&V.timestamp-ye.timestamp>r;(me||B)&&tt(!0)}};const se=_=>{J({type:U.IncrementalSnapshot,data:{source:D.Mutation,..._}})},Me=_=>J({type:U.IncrementalSnapshot,data:{source:D.Scroll,..._}}),de=_=>J({type:U.IncrementalSnapshot,data:{source:D.CanvasMutation,..._}}),De=_=>J({type:U.IncrementalSnapshot,data:{source:D.AdoptedStyleSheet,..._}}),ae=new va({mutationCb:se,adoptedStyleSheetCb:De}),le=new ha({mirror:ce,mutationCb:se,stylesheetManager:ae,recordCrossOriginIframes:O,wrappedEmit:J});for(const _ of R||[])_.getMirror&&_.getMirror({nodeMirror:ce,crossOriginIframeMirror:le.crossOriginIframeMirror,crossOriginIframeStyleMirror:le.crossOriginIframeStyleMirror});const X=new Ca;Or=new Sa({recordCanvas:x,mutationCb:de,win:window,blockClass:s,blockSelector:f,mirror:ce,sampling:S.canvas,dataURLOptions:b});const ne=new fa({mutationCb:se,scrollCb:Me,bypassOptions:{blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskInputOptions:W,dataURLOptions:b,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,recordCanvas:x,inlineImages:k,sampling:S,slimDOMOptions:P,iframeManager:le,stylesheetManager:ae,canvasManager:Or,keepIframeSrcFn:Z,processedNodeManager:X},mirror:ce});tt=(_=!1)=>{if(!v)return;J({type:U.Meta,data:{href:window.location.href,width:qi(),height:Wi()}},_),ae.reset(),ne.init(),ve.forEach(G=>G.lock());const ue=Zn(document,{mirror:ce,blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskAllInputs:W,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,slimDOM:P,dataURLOptions:b,recordCanvas:x,inlineImages:k,applyBackgroundColorToBlockedElements:H,onSerialize:G=>{Gi(G,ce)&&le.addIframe(G),Vi(G,ce)&&ae.trackLinkElement(G),Ir(G)&&ne.addShadowRoot(L.shadowRoot(G),document)},onIframeLoad:(G,V)=>{le.attachIframe(G,V),ne.observeAttachShadow(G)},onStylesheetLoad:(G,V)=>{ae.attachLinkElement(G,V)},keepIframeSrcFn:Z});if(!ue)return console.warn("Failed to snapshot the document");J({type:U.FullSnapshot,data:{node:ue,initialOffset:zi(window)}},_),ve.forEach(G=>G.unlock()),document.adoptedStyleSheets&&document.adoptedStyleSheets.length>0&&ae.adoptStyleSheets(document.adoptedStyleSheets,ce.getId(document))};try{const _=[],ue=V=>{var me;return T(ca)({mutationCb:se,mousemoveCb:(B,vt)=>J({type:U.IncrementalSnapshot,data:{source:vt,positions:B}}),mouseInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MouseInteraction,...B}}),scrollCb:Me,viewportResizeCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.ViewportResize,...B}}),inputCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Input,...B}}),mediaInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MediaInteraction,...B}}),styleSheetRuleCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleSheetRule,...B}}),styleDeclarationCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleDeclaration,...B}}),canvasMutationCb:de,fontCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Font,...B}}),selectionCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.Selection,...B}})},customElementCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.CustomElement,...B}})},blockClass:s,ignoreClass:u,ignoreSelector:m,maskTextClass:a,maskTextSelector:p,maskInputOptions:W,inlineStylesheet:i,sampling:S,recordDOM:v,recordCanvas:x,inlineImages:k,userTriggeredOnInput:M,collectFonts:$,doc:V,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,keepIframeSrcFn:Z,blockSelector:f,slimDOMOptions:P,dataURLOptions:b,mirror:ce,iframeManager:le,stylesheetManager:ae,shadowDomManager:ne,processedNodeManager:X,canvasManager:Or,ignoreCSSAttributes:N,plugins:((me=R?.filter(B=>B.observer))==null?void 0:me.map(B=>({observer:B.observer,options:B.options,callback:vt=>J({type:U.Plugin,data:{plugin:B.name,payload:vt}})})))||[]},C)};le.addLoadListener(V=>{try{_.push(ue(V.contentDocument))}catch(me){console.warn(me)}});const G=()=>{tt(),_.push(ue(document)),ut=!0};return document.readyState==="interactive"||document.readyState==="complete"?G():(_.push(te("DOMContentLoaded",()=>{J({type:U.DomContentLoaded,data:{}}),A==="DOMContentLoaded"&&G()})),_.push(te("load",()=>{J({type:U.Load,data:{}}),A==="load"&&G()},window))),()=>{_.forEach(V=>V()),X.destroy(),ut=!1,Xo()}}catch(_){console.warn(_)}}Oe.addCustomEvent=(e,t)=>{if(!ut)throw new Error("please add custom event after start recording");J({type:U.Custom,data:{tag:e,payload:t}})};Oe.freezePage=()=>{ve.forEach(e=>e.freeze())};Oe.takeFullSnapshot=e=>{if(!ut)throw new Error("please take full snapshot after start recording");tt(e)};Oe.mirror=ce;var yi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(yi||(yi={}));const{addCustomEvent:xa}=Oe,{freezePage:Ra}=Oe,{takeFullSnapshot:Oa}=Oe;export{Oe as record}; - -``` - ---- - -## .output/public/_nuxt/MmvQX_eT.js - -```js -import{f as m,g as f,p as h,h as b,q as _,c as i,a as s,i as n,t as u,l as v,n as C,r as y,o as c}from"./BMw6m4EL.js";const k={class:"container"},T={style:{"text-align":"center"}},g={key:0,class:"success"},x={class:"stats"},N=m({__name:"burrito",setup(B){const a=f(),o=C(()=>a.user.value),l=h(),r=y(!1),{$amplitude:d}=b();_(()=>{o.value||l.push("/")});const p=async()=>{if(o.value)try{const e=await $fetch("/api/burrito/consider",{method:"POST",body:{username:o.value.username}});e.success&&e.user&&(a.setUser(e.user),r.value=!0,d.track("burrito_considered",{total_considerations:e.user.burritoConsiderations,username:e.user.username}),setTimeout(()=>{r.value=!1},2e3))}catch(e){console.error("Error considering burrito:",e)}};return(e,t)=>(c(),i("div",k,[t[1]||(t[1]=s("h1",null,"Burrito consideration zone",-1)),t[2]||(t[2]=s("p",null,"Take a moment to truly consider the potential of burritos.",-1)),s("div",T,[s("button",{onClick:p,class:"btn-burrito"}," I have considered the burrito potential "),n(r)?(c(),i("p",g," Thank you for your consideration! Count: "+u(n(o)?.burritoConsiderations),1)):v("",!0)]),s("div",x,[t[0]||(t[0]=s("h3",null,"Consideration stats",-1)),s("p",null,"Total considerations: "+u(n(o)?.burritoConsiderations),1)])]))}});export{N as default}; - -``` - ---- - -## .output/public/_nuxt/MzFTpsyS.js - -```js -import{_ as a,u as i,o as u,c,a as e,t as r,b as l,w as d,d as p,e as f}from"./BMw6m4EL.js";const m={class:"antialiased bg-white dark:bg-black dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-black"},g={class:"max-w-520px text-center z-20"},b=["textContent"],h=["textContent"],x={class:"flex items-center justify-center w-full"},y={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},status:{type:Number,default:404},statusText:{type:String,default:"Not Found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(t){const n=t;return i({title:`${n.status} - ${n.statusText} | ${n.appName}`,script:[{innerHTML:`!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`}],style:[{innerHTML:'*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}h1,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }'}]}),(k,o)=>{const s=f;return u(),c("div",m,[o[0]||(o[0]=e("div",{class:"fixed left-0 right-0 spotlight z-10"},null,-1)),e("div",g,[e("h1",{class:"font-medium mb-8 sm:text-10xl text-8xl",textContent:r(t.status)},null,8,b),e("p",{class:"font-light leading-tight mb-16 px-8 sm:px-0 sm:text-4xl text-xl",textContent:r(t.description)},null,8,h),e("div",x,[l(s,{to:"/",class:"cursor-pointer gradient-border px-4 py-2 sm:px-6 sm:py-3 sm:text-xl text-md"},{default:d(()=>[p(r(t.backHome),1)]),_:1})])])])}}},_=a(y,[["__scopeId","data-v-42b740d7"]]);export{_ as default}; - -``` - ---- - -## .output/public/_nuxt/RzjnwIMW.js - -```js -import{s as N,x as A,z as U,A as O}from"./BMw6m4EL.js";var L=function(a,e){return L=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,t){r.__proto__=t}||function(r,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(r[n]=t[n])},L(a,e)};function k(a,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");L(a,e);function r(){this.constructor=a}a.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var d=function(){return d=Object.assign||function(e){for(var r,t=1,n=arguments.length;t=a.length&&(a=void 0),{value:a&&a[t++],done:!a}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function D(a,e){var r=typeof Symbol=="function"&&a[Symbol.iterator];if(!r)return a;var t=r.call(a),n,o=[],i;try{for(;(e===void 0||e-- >0)&&!(n=t.next()).done;)o.push(n.value)}catch(c){i={error:c}}finally{try{n&&!n.done&&(r=t.return)&&r.call(t)}finally{if(i)throw i.error}}return o}function P(a,e,r){if(arguments.length===2)for(var t=0,n=e.length,o;t>6|192,e[r++]=n&63|128):(n&64512)==55296&&t+1>18|240,e[r++]=n>>12&63|128,e[r++]=n>>6&63|128,e[r++]=n&63|128):(e[r++]=n>>12|224,e[r++]=n>>6&63|128,e[r++]=n&63|128)}return Uint8Array.from(e)},R=-862048943,I=461845907,x=15,M=13,Q=5,B=-430675100,F=function(a,e){e===void 0&&(e=0);for(var r=j(a),t=r.length,n=t>>2,o=e,i=0;i>>0},X=function(a,e){var r=a,t=e;return r=Math.imul(r,R),r=m(r,x),r=Math.imul(r,I),t^=r,t=m(t,M),t=Math.imul(t,Q),t+B|0},Y=function(a){var e=a;return e^=e>>>16,e=Math.imul(e,-2048144789),e^=e>>>13,e=Math.imul(e,-1028477387),e^=e>>>16,e},m=function(a,e,r){r===void 0&&(r=32),e>r&&(e=e%r);var t=4294967295<>>0,n=(a&t)>>>0>>>r-e>>>0;return(a<>>0},q=function(a,e){e===void 0&&(e=0);var r=a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3];return z(r)},z=function(a){return(a&-16777216)>>>24|(a&16711680)>>>8|(a&65280)<<8|(a&255)<<24},H=function(a,e){var r,t;if(!(!e||e.length===0)){try{for(var n=y(e),o=n.next();!o.done;o=n.next()){var i=o.value;if(!i||!a||typeof a!="object")return;a=a[i]}}catch(c){r={error:c}}finally{try{o&&!o.done&&(t=n.return)&&t.call(n)}finally{if(r)throw r.error}}if(a)return a}},J="(\\d+)\\.(\\d+)",K="(\\d+)",$="(-(([-\\w]+\\.?)*))?",W="^".concat(J,"(\\.").concat(K).concat($,")?$"),Z=(function(){function a(e,r,t,n){n===void 0&&(n=void 0),this.major=e,this.minor=r,this.patch=t,this.preRelease=n}return a.parse=function(e){if(e){var r=new RegExp(W).exec(e);if(r){var t=Number(r[1]),n=Number(r[2]);if(!(isNaN(t)||isNaN(n))){var o=Number(r[4])||0,i=r[5]||void 0;return new a(t,n,o,i)}}}},a.prototype.compareTo=function(e){return this.major>e.major?1:this.majore.minor?1:this.minore.patch?1:this.patche.preRelease?1:this.preRelease=l&&u=b&&St;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return e>=t;default:return!1}},a.prototype.versionComparator=function(e,r,t){var n=e.compareTo(t);switch(r){case s.LESS_THAN:case s.VERSION_LESS_THAN:return n<0;case s.LESS_THAN_EQUALS:case s.VERSION_LESS_THAN_EQUALS:return n<=0;case s.GREATER_THAN:case s.VERSION_GREATER_THAN:return n>0;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return n>=0;default:return!1}},a.prototype.matchesRegex=function(e,r){return r.some(function(t){return!!new RegExp(t).exec(e)})},a.prototype.containsNone=function(e){return e.some(function(r){return r==="(none)"})},a.prototype.containsBooleans=function(e){return e.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},a.prototype.parseNumber=function(e){var r;return(r=Number(e))!==null&&r!==void 0?r:void 0},a.prototype.coerceString=function(e){if(e)return typeof e=="object"?JSON.stringify(e):String(e)},a.prototype.coerceStringArray=function(e){var r=this;if(Array.isArray(e)){var t=e;return t.map(function(i){return r.coerceString(i)}).filter(Boolean)}var n=String(e);try{var o=JSON.parse(n);if(Array.isArray(o)){var t=e;return t.map(function(c){return r.coerceString(c)}).filter(Boolean)}else return}catch{return}},a.prototype.isSetOperator=function(e){switch(e){case s.SET_IS:case s.SET_IS_NOT:case s.SET_CONTAINS:case s.SET_DOES_NOT_CONTAIN:case s.SET_CONTAINS_ANY:case s.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},a.prototype.setEquals=function(e,r){var t=new Set(e),n=new Set(r);return t.size===n.size&&P([],D(n),!1).every(function(o){return t.has(o)})},a.prototype.matchesSetContainsAll=function(e,r){var t,n;if(e.length{let e={};return a.forEach((r,t)=>e[r]=t),e})(re);String.fromCharCode.bind(String);typeof Uint8Array.from=="function"&&Uint8Array.from.bind(Uint8Array);(function(a){k(e,a);function e(r,t){var n=a.call(this,t)||this;return n.statusCode=r,Object.setPrototypeOf(n,e.prototype),n}return e})(Error);var te=1e3*60*60*24*2,ne=(function(){function a(){var e=this;this.dbs={},this.createStore=function(r){return N(e,void 0,void 0,function(){return A(this,function(t){switch(t.label){case 0:return[4,U(r,1,{upgrade:function(n){n.objectStoreNames.contains("eventTypesForSession")||n.createObjectStore("eventTypesForSession",{keyPath:"sessionId"})}})];case 1:return[2,t.sent()]}})})},this.openOrCreateDB=function(r){return N(e,void 0,void 0,function(){var t,n;return A(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(t="".concat(r.substring(0,10),"_amp_targeting"),[4,this.createStore(t)]);case 1:return n=o.sent(),this.dbs[r]=n,[2,n]}})})},this.updateEventListForSession=function(r){var t=r.sessionId,n=r.eventType,o=r.eventTime,i=r.loggerProvider,c=r.tx;return N(e,void 0,void 0,function(){var v,f,u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,c.store.get(t)];case 1:return v=l.sent(),f=v?v.eventTypes:{},u=f[n]||{},S=O(O({},f),(E={},E[n]=O(O({},u),(T={},T[o]={event_type:n},T)),E)),[4,c.store.put({sessionId:t,eventTypes:S})];case 2:return l.sent(),[2,S];case 3:return h=l.sent(),i.warn("Failed to store events for targeting ".concat(t,": ").concat(h)),[3,4];case 4:return[2,void 0]}})})},this.deleteOldSessionEventTypes=function(r){var t=r.currentSessionId,n=r.loggerProvider,o=r.tx;return N(e,void 0,void 0,function(){var i,c,v,f,u;return A(this,function(S){switch(S.label){case 0:return S.trys.push([0,6,,7]),[4,o.store.getAll()];case 1:i=S.sent(),c=0,S.label=2;case 2:return cte?[4,o.store.delete(v.sessionId)]:[3,4]):[3,5];case 3:S.sent(),S.label=4;case 4:return c++,[3,2];case 5:return[3,7];case 6:return u=S.sent(),n.warn("Failed to clear old session events for targeting: ".concat(u)),[3,7];case 7:return[2]}})})},this.storeEventTypeForSession=function(r){var t=r.loggerProvider,n=r.sessionId,o=r.eventType,i=r.eventTime,c=r.apiKey;return N(e,void 0,void 0,function(){var v,f,u,S;return A(this,function(h){switch(h.label){case 0:return h.trys.push([0,5,,6]),[4,this.openOrCreateDB(c)];case 1:return v=h.sent(),f=v.transaction("eventTypesForSession","readwrite"),f?[4,this.updateEventListForSession({sessionId:n,tx:f,loggerProvider:t,eventType:o,eventTime:i})]:[2];case 2:return u=h.sent(),[4,this.deleteOldSessionEventTypes({currentSessionId:n,tx:f,loggerProvider:t})];case 3:return h.sent(),[4,f.done];case 4:return h.sent(),[2,u];case 5:return S=h.sent(),t.warn("Failed to store events for targeting ".concat(n,": ").concat(S)),[3,6];case 6:return[2,void 0]}})})}}return a})(),ae=new ne,oe=(function(){function a(){var e=this;this.evaluateTargeting=function(r){var t=r.apiKey,n=r.loggerProvider,o=r.event,i=r.sessionId,c=r.userProperties,v=r.deviceId,f=r.flag;return N(e,void 0,void 0,function(){var u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return o&&o.time?[4,ae.storeEventTypeForSession({loggerProvider:n,apiKey:t,sessionId:i,eventType:o.event_type,eventTime:o.time})]:[3,2];case 1:return S=l.sent(),[3,3];case 2:S=void 0,l.label=3;case 3:return u=S,h=u&&new Set(Object.keys(u)),E={session_id:i,event:o,event_types:h&&Array.from(h),user:{device_id:v,user_properties:c}},T=this.evaluationEngine.evaluate(E,[f]),[2,T]}})})},this.evaluationEngine=new V}return a})(),ie=function(){var a=new oe;return{evaluateTargeting:a.evaluateTargeting.bind(a)}};const se=ie();var ce=se.evaluateTargeting;export{ce as evaluateTargeting}; - -``` - ---- - -## .output/public/_nuxt/sPdhR0i8.js - -```js -var me={},ki=Object.defineProperty,qi=(e,o,p)=>o in e?ki(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,le=(e,o,p)=>qi(e,typeof o!="symbol"?o+"":o,p),Fi=Object.defineProperty,Bi=(e,o,p)=>o in e?Fi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,_e=(e,o,p)=>Bi(e,typeof o!="symbol"?o+"":o,p),tr,zi=Object.defineProperty,ji=(e,o,p)=>o in e?zi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,rr=(e,o,p)=>ji(e,typeof o!="symbol"?o+"":o,p);const ir={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},sr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},ae={};function Wi(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function kt(e){if(ae[e])return ae[e];const o=Wi(e)||globalThis[e],p=o.prototype,d=e in ir?ir[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in sr?sr[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return ae[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?ae[e]=h:p}catch{return p}}const Ie={};function W(e,o,p){var d;const s=`${e}.${String(p)}`;if(Ie[s])return Ie[s].call(o);const f=kt(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(Ie[s]=a,a.call(o)):o[p]}const Le={};function pi(e,o,p){const d=`${e}.${String(p)}`;if(Le[d])return Le[d].bind(o);const f=kt(e)[p];return typeof f!="function"?o[p]:(Le[d]=f,f.bind(o))}function Hi(e){return W("Node",e,"childNodes")}function Gi(e){return W("Node",e,"parentNode")}function Ji(e){return W("Node",e,"parentElement")}function Vi(e){return W("Node",e,"textContent")}function Ki(e,o){return pi("Node",e,"contains")(o)}function Qi(e){return pi("Node",e,"getRootNode")()}function Yi(e){return!e||!("host"in e)?null:W("ShadowRoot",e,"host")}function Xi(e){return e.styleSheets}function Zi(e){return!e||!("shadowRoot"in e)?null:W("Element",e,"shadowRoot")}function es(e,o){return W("Element",e,"querySelector")(o)}function ts(e,o){return W("Element",e,"querySelectorAll")(o)}function rs(){return kt("MutationObserver").constructor}const ge={childNodes:Hi,parentNode:Gi,parentElement:Ji,textContent:Vi,contains:Ki,getRootNode:Qi,host:Yi,styleSheets:Xi,shadowRoot:Zi,querySelector:es,querySelectorAll:ts,mutationObserver:rs};function is(e){const o=e&&"host"in e&&"mode"in e&&ge.host(e)||null;return!!(o&&"shadowRoot"in o&&ge.shadowRoot(o)===e)}class ss{constructor(){rr(this,"idNodeMap",new Map),rr(this,"nodeMetaMap",new WeakMap)}getId(o){var p;return o?((p=this.getMeta(o))==null?void 0:p.id)??-1:-1}getNode(o){return this.idNodeMap.get(o)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(o){return this.nodeMetaMap.get(o)||null}removeNodeFromMap(o){const p=this.getId(o);this.idNodeMap.delete(p),o.childNodes&&o.childNodes.forEach(d=>this.removeNodeFromMap(d))}has(o){return this.idNodeMap.has(o)}hasNode(o){return this.nodeMetaMap.has(o)}add(o,p){const d=p.id;this.idNodeMap.set(d,o),this.nodeMetaMap.set(o,p)}replace(o,p){const d=this.getNode(o);if(d){const s=this.nodeMetaMap.get(d);s&&this.nodeMetaMap.set(p,s)}this.idNodeMap.set(o,p)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function ns(){return new ss}const os=-2;function Tt(e,o,p){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return p?Tt(ge.parentNode(e),o,p):!1;for(let d=e.classList.length;d--;){const s=e.classList[d];if(o.test(s))return!0}return p?Tt(ge.parentNode(e),o,p):!1}function ls(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function as(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var ue={exports:{}},nr;function us(){if(nr)return ue.exports;nr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return ue.exports=o(),ue.exports.createColors=o,ue.exports}const fs={},hs=Object.freeze(Object.defineProperty({__proto__:null,default:fs},Symbol.toStringTag,{value:"Module"})),F=as(hs);var $e,or;function qt(){if(or)return $e;or=1;let e=us(),o=F;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return $e=p,p.default=p,$e}var fe={},lr;function Ft(){return lr||(lr=1,fe.isClean=Symbol("isClean"),fe.my=Symbol("my")),fe}var Te,ar;function di(){if(ar)return Te;ar=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Te=p,p.default=p,Te}var Ue,ur;function ye(){if(ur)return Ue;ur=1;let e=di();function o(p,d){new e(d).stringify(p)}return Ue=o,o.default=o,Ue}var De,fr;function we(){if(fr)return De;fr=1;let{isClean:e,my:o}=Ft(),p=qt(),d=di(),s=ye();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return De=a,a.default=a,De}var ke,hr;function be(){if(hr)return ke;hr=1;let e=we();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ke=o,o.default=o,ke}var qe,cr;function cs(){if(cr)return qe;cr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return qe={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},qe}var Fe,pr;function mi(){if(pr)return Fe;pr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{existsSync:p,readFileSync:d}=F,{dirname:s,join:f}=F;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return Fe=m,m.default=m,Fe}var Be,dr;function ve(){if(dr)return Be;dr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{fileURLToPath:p,pathToFileURL:d}=F,{isAbsolute:s,resolve:f}=F,{nanoid:a}=cs(),m=F,n=qt(),h=mi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return Be=r,r.default=r,m&&m.registerInput&&m.registerInput(r),Be}var ze,mr;function gi(){if(mr)return ze;mr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{dirname:p,relative:d,resolve:s,sep:f}=F,{pathToFileURL:a}=F,m=ve(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return ze=t,ze}var je,gr;function xe(){if(gr)return je;gr=1;let e=we();class o extends e{constructor(d){super(d),this.type="comment"}}return je=o,o.default=o,je}var We,yr;function Q(){if(yr)return We;yr=1;let{isClean:e,my:o}=Ft(),p=be(),d=xe(),s=we(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},We=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},We}var He,wr;function Bt(){if(wr)return He;wr=1;let e=Q(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},He=d,d.default=d,He}var Ge,br;function yi(){if(br)return Ge;br=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Ge=e,e.default=e,Ge}var Je,vr;function zt(){if(vr)return Je;vr=1;let e=yi();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return Je=o,o.default=o,Je}var Ve,xr;function ps(){if(xr)return Ve;xr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return Ve=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},Ve}var Ke,Sr;function jt(){if(Sr)return Ke;Sr=1;let e=Q();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return Ke=o,o.default=o,e.registerAtRule(o),Ke}var Qe,Rr;function ne(){if(Rr)return Qe;Rr=1;let e=Q(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},Qe=d,d.default=d,e.registerRoot(d),Qe}var Ye,Or;function wi(){if(Or)return Ye;Or=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Ye=e,e.default=e,Ye}var Xe,Cr;function Wt(){if(Cr)return Xe;Cr=1;let e=Q(),o=wi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Xe=p,p.default=p,e.registerRule(p),Xe}var Ze,Er;function ds(){if(Er)return Ze;Er=1;let e=be(),o=ps(),p=xe(),d=jt(),s=ne(),f=Wt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},tt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),tt}var rt,Nr;function ms(){if(Nr)return rt;Nr=1;let e=gi(),o=ye(),p=Ht();const d=zt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return rt=s,s.default=s,rt}var it,Mr;function gs(){if(Mr)return it;Mr=1;let e=ms(),o=bi(),p=Bt(),d=ne();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return it=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),it}var st,_r;function ys(){if(_r)return st;_r=1;let e=be(),o=mi(),p=xe(),d=jt(),s=ve(),f=ne(),a=Wt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return st=m,m.default=m,st}var nt,Ir;function ws(){if(Ir)return nt;Ir=1;let e=qt(),o=be(),p=bi(),d=Q(),s=gs(),f=ye(),a=ys(),m=Bt(),n=yi(),h=xe(),t=jt(),l=zt(),i=ve(),r=Ht(),c=wi(),u=Wt(),g=ne(),S=we();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),nt=b,b.default=b,nt}var bs=ws();const _=ls(bs);_.stringify;_.fromJSON;_.plugin;_.parse;_.list;_.document;_.comment;_.atRule;_.rule;_.decl;_.root;_.CssSyntaxError;_.Declaration;_.Container;_.Processor;_.Document;_.Comment;_.Warning;_.AtRule;_.Result;_.Input;_.Rule;_.Root;_.Node;var vs=Object.defineProperty,xs=(e,o,p)=>o in e?vs(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,k=(e,o,p)=>xs(e,typeof o!="symbol"?o+"":o,p);function Ss(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Rs(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var he={exports:{}},Lr;function Os(){if(Lr)return he.exports;Lr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return he.exports=o(),he.exports.createColors=o,he.exports}const Cs={},Es=Object.freeze(Object.defineProperty({__proto__:null,default:Cs},Symbol.toStringTag,{value:"Module"})),B=Rs(Es);var ot,$r;function Gt(){if($r)return ot;$r=1;let e=Os(),o=B;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return ot=p,p.default=p,ot}var ce={},Tr;function Jt(){return Tr||(Tr=1,ce.isClean=Symbol("isClean"),ce.my=Symbol("my")),ce}var lt,Ur;function vi(){if(Ur)return lt;Ur=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return lt=p,p.default=p,lt}var at,Dr;function Se(){if(Dr)return at;Dr=1;let e=vi();function o(p,d){new e(d).stringify(p)}return at=o,o.default=o,at}var ut,kr;function Re(){if(kr)return ut;kr=1;let{isClean:e,my:o}=Jt(),p=Gt(),d=vi(),s=Se();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return ut=a,a.default=a,ut}var ft,qr;function Oe(){if(qr)return ft;qr=1;let e=Re();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ft=o,o.default=o,ft}var ht,Fr;function Ps(){if(Fr)return ht;Fr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return ht={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},ht}var ct,Br;function xi(){if(Br)return ct;Br=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{existsSync:p,readFileSync:d}=B,{dirname:s,join:f}=B;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return ct=m,m.default=m,ct}var pt,zr;function Ce(){if(zr)return pt;zr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{fileURLToPath:p,pathToFileURL:d}=B,{isAbsolute:s,resolve:f}=B,{nanoid:a}=Ps(),m=B,n=Gt(),h=xi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return pt=r,r.default=r,m&&m.registerInput&&m.registerInput(r),pt}var dt,jr;function Si(){if(jr)return dt;jr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{dirname:p,relative:d,resolve:s,sep:f}=B,{pathToFileURL:a}=B,m=Ce(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return dt=t,dt}var mt,Wr;function Ee(){if(Wr)return mt;Wr=1;let e=Re();class o extends e{constructor(d){super(d),this.type="comment"}}return mt=o,o.default=o,mt}var gt,Hr;function Y(){if(Hr)return gt;Hr=1;let{isClean:e,my:o}=Jt(),p=Oe(),d=Ee(),s=Re(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},gt=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},gt}var yt,Gr;function Vt(){if(Gr)return yt;Gr=1;let e=Y(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},yt=d,d.default=d,yt}var wt,Jr;function Ri(){if(Jr)return wt;Jr=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return wt=e,e.default=e,wt}var bt,Vr;function Kt(){if(Vr)return bt;Vr=1;let e=Ri();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return bt=o,o.default=o,bt}var vt,Kr;function As(){if(Kr)return vt;Kr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return vt=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},vt}var xt,Qr;function Qt(){if(Qr)return xt;Qr=1;let e=Y();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return xt=o,o.default=o,e.registerAtRule(o),xt}var St,Yr;function oe(){if(Yr)return St;Yr=1;let e=Y(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},St=d,d.default=d,e.registerRoot(d),St}var Rt,Xr;function Oi(){if(Xr)return Rt;Xr=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Rt=e,e.default=e,Rt}var Ot,Zr;function Yt(){if(Zr)return Ot;Zr=1;let e=Y(),o=Oi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Ot=p,p.default=p,e.registerRule(p),Ot}var Ct,ei;function Ns(){if(ei)return Ct;ei=1;let e=Oe(),o=As(),p=Ee(),d=Qt(),s=oe(),f=Yt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},Pt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),Pt}var At,ii;function Ms(){if(ii)return At;ii=1;let e=Si(),o=Se(),p=Xt();const d=Kt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return At=s,s.default=s,At}var Nt,si;function _s(){if(si)return Nt;si=1;let e=Ms(),o=Ci(),p=Vt(),d=oe();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return Nt=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),Nt}var Mt,ni;function Is(){if(ni)return Mt;ni=1;let e=Oe(),o=xi(),p=Ee(),d=Qt(),s=Ce(),f=oe(),a=Yt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return Mt=m,m.default=m,Mt}var _t,oi;function Ls(){if(oi)return _t;oi=1;let e=Gt(),o=Oe(),p=Ci(),d=Y(),s=_s(),f=Se(),a=Is(),m=Vt(),n=Ri(),h=Ee(),t=Qt(),l=Kt(),i=Ce(),r=Xt(),c=Oi(),u=Yt(),g=oe(),S=Re();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),_t=b,b.default=b,_t}var $s=Ls();const I=Ss($s);I.stringify;I.fromJSON;I.plugin;I.parse;I.list;I.document;I.comment;I.atRule;I.rule;I.decl;I.root;I.CssSyntaxError;I.Declaration;I.Container;I.Processor;I.Document;I.Comment;I.Warning;I.AtRule;I.Result;I.Input;I.Rule;I.Root;I.Node;class Zt{constructor(...o){k(this,"parentElement",null),k(this,"parentNode",null),k(this,"ownerDocument"),k(this,"firstChild",null),k(this,"lastChild",null),k(this,"previousSibling",null),k(this,"nextSibling",null),k(this,"ELEMENT_NODE",1),k(this,"TEXT_NODE",3),k(this,"nodeType"),k(this,"nodeName"),k(this,"RRNodeType")}get childNodes(){const o=[];let p=this.firstChild;for(;p;)o.push(p),p=p.nextSibling;return o}contains(o){if(o instanceof Zt){if(o.ownerDocument!==this.ownerDocument)return!1;if(o===this)return!0}else return!1;for(;o.parentNode;){if(o.parentNode===this)return!0;o=o.parentNode}return!1}appendChild(o){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(o,p){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(o){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const li={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ai={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},pe={};function Ts(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function er(e){if(pe[e])return pe[e];const o=Ts(e)||globalThis[e],p=o.prototype,d=e in li?li[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in ai?ai[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return pe[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?pe[e]=h:p}catch{return p}}const It={};function H(e,o,p){var d;const s=`${e}.${String(p)}`;if(It[s])return It[s].call(o);const f=er(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(It[s]=a,a.call(o)):o[p]}const Lt={};function Ei(e,o,p){const d=`${e}.${String(p)}`;if(Lt[d])return Lt[d].bind(o);const f=er(e)[p];return typeof f!="function"?o[p]:(Lt[d]=f,f.bind(o))}function Us(e){return H("Node",e,"childNodes")}function Ds(e){return H("Node",e,"parentNode")}function ks(e){return H("Node",e,"parentElement")}function qs(e){return H("Node",e,"textContent")}function Fs(e,o){return Ei("Node",e,"contains")(o)}function Bs(e){return Ei("Node",e,"getRootNode")()}function zs(e){return!e||!("host"in e)?null:H("ShadowRoot",e,"host")}function js(e){return e.styleSheets}function Ws(e){return!e||!("shadowRoot"in e)?null:H("Element",e,"shadowRoot")}function Hs(e,o){return H("Element",e,"querySelector")(o)}function Gs(e,o){return H("Element",e,"querySelectorAll")(o)}function Js(){return er("MutationObserver").constructor}const q={childNodes:Us,parentNode:Ds,parentElement:ks,textContent:qs,contains:Fs,getRootNode:Bs,host:zs,styleSheets:js,shadowRoot:Ws,querySelector:Hs,querySelectorAll:Gs,mutationObserver:Js};function Vs(e,o,p=document){const d={capture:!0,passive:!0};return p.addEventListener(e,o,d),()=>p.removeEventListener(e,o,d)}const ee=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let Ut={map:{},getId(){return console.error(ee),-1},getNode(){return console.error(ee),null},removeNodeFromMap(){console.error(ee)},has(){return console.error(ee),!1},reset(){console.error(ee)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(Ut=new Proxy(Ut,{get(e,o,p){return o==="map"&&console.error(ee),Reflect.get(e,o,p)}}));function Ks(e,o,p={}){let d=null,s=0;return function(...f){const a=Date.now();!s&&p.leading===!1&&(s=a);const m=o-(a-s),n=this;m<=0||m>o?(d&&(clearTimeout(d),d=null),s=a,e.apply(n,f)):!d&&p.trailing!==!1&&(d=setTimeout(()=>{s=p.leading===!1?0:Date.now(),d=null,e.apply(n,f)},m))}}function Pi(e,o,p,d,s=window){const f=s.Object.getOwnPropertyDescriptor(e,o);return s.Object.defineProperty(e,o,d?p:{set(a){setTimeout(()=>{p.set.call(this,a)},0),f&&f.set&&f.set.call(this,a)}}),()=>Pi(e,o,f||{},!0)}function Qs(e,o,p){try{if(!(o in e))return()=>{};const d=e[o],s=p(d);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:d}})),e[o]=s,()=>{e[o]=d}}catch{return()=>{}}}let Ai=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(Ai=()=>new Date().getTime());function Ys(e){var o,p,d,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((o=q.parentElement(f.body))==null?void 0:o.scrollLeft)||((p=f?.body)==null?void 0:p.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((d=q.parentElement(f.body))==null?void 0:d.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Xs(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function Zs(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function Ni(e){return e?e.nodeType===e.ELEMENT_NODE?e:q.parentElement(e):null}function en(e,o,p,d){if(!e)return!1;const s=Ni(e);if(!s)return!1;try{if(typeof o=="string"){if(s.classList.contains(o)||d&&s.closest("."+o)!==null)return!0}else if(Tt(s,o,d))return!0}catch{}return!!(p&&(s.matches(p)||d&&s.closest(p)!==null))}function tn(e,o){return o.getId(e)!==-1}function rn(e,o,p){return e.tagName==="TITLE"&&p.headTitleMutations?!0:o.getId(e)===os}function Mi(e,o){if(is(e))return!1;const p=o.getId(e);if(!o.has(p))return!0;const d=q.parentNode(e);return d&&d.nodeType===e.DOCUMENT_NODE?!1:d?Mi(d,o):!0}function sn(e){return!!e.changedTouches}function nn(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function on(e){const o={},p=(s,f)=>{const a={value:s,parent:f,children:[]};return o[s.node.id]=a,a},d=[];for(const s of e){const{nextId:f,parentId:a}=s;if(f&&f in o){const m=o[f];if(m.parent){const n=m.parent.children.indexOf(m);m.parent.children.splice(n,0,p(s,m.parent))}else{const n=d.indexOf(m);d.splice(n,0,p(s,null))}continue}if(a in o){const m=o[a];m.children.push(p(s,m));continue}d.push(p(s,null))}return d}function _i(e,o){o(e.value);for(let p=e.children.length-1;p>=0;p--)_i(e.children[p],o)}function ln(e,o){return!!(e.nodeName==="IFRAME"&&o.getMeta(e))}function an(e,o){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&o.getMeta(e))}function Ii(e,o){var p,d;const s=(d=(p=e.ownerDocument)==null?void 0:p.defaultView)==null?void 0:d.frameElement;if(!s||s===o)return{x:0,y:0,relativeScale:1,absoluteScale:1};const f=s.getBoundingClientRect(),a=Ii(s,o),m=f.height/s.clientHeight;return{x:f.x*a.relativeScale+a.x,y:f.y*a.relativeScale+a.y,relativeScale:m,absoluteScale:a.absoluteScale*m}}function un(e){return e?e instanceof Zt&&"shadowRoot"in e?!!e.shadowRoot:!!q.shadowRoot(e):!1}function Li(e,o){const p=e[o[0]];return o.length===1?p:Li(p.cssRules[o[1]].cssRules,o.slice(2))}function fn(e){const o=[...e],p=o.pop();return{positions:o,index:p}}function hn(e){const o=new Set,p=[];for(let d=e.length;d--;){const s=e[d];o.has(s.id)||(p.push(s),o.add(s.id))}return p}class cn{constructor(){_e(this,"id",1),_e(this,"styleIDMap",new WeakMap),_e(this,"idStyleMap",new Map)}getId(o){return this.styleIDMap.get(o)??-1}has(o){return this.styleIDMap.has(o)}add(o,p){if(this.has(o))return this.getId(o);let d;return p===void 0?d=this.id++:d=p,this.styleIDMap.set(o,d),this.idStyleMap.set(d,o),d}getStyle(o){return this.idStyleMap.get(o)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function $i(e){var o;let p=null;return"getRootNode"in e&&((o=q.getRootNode(e))==null?void 0:o.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&q.host(q.getRootNode(e))&&(p=q.host(q.getRootNode(e))),p}function Ti(e){let o=e,p;for(;p=$i(o);)o=p;return o}function Ui(e){const o=e.ownerDocument;if(!o)return!1;const p=Ti(e);return q.contains(o,p)}function pn(e){const o=e.ownerDocument;return o?q.contains(o,e)||Ui(e):!1}const dn=Object.freeze(Object.defineProperty({__proto__:null,StyleSheetMirror:cn,get _mirror(){return Ut},closestElementOfNode:Ni,getBaseDimension:Ii,getNestedRule:Li,getPositionsAndIndex:fn,getRootShadowHost:Ti,getShadowHost:$i,getWindowHeight:Xs,getWindowScroll:Ys,getWindowWidth:Zs,hasShadowRoot:un,hookSetter:Pi,inDom:pn,isAncestorRemoved:Mi,isBlocked:en,isIgnored:rn,isSerialized:tn,isSerializedIframe:ln,isSerializedStylesheet:an,iterateResolveTree:_i,legacy_isTouchEvent:sn,get nowTimestamp(){return Ai},on:Vs,patch:Qs,polyfill:nn,queueToResolveTrees:on,shadowHostInDom:Ui,throttle:Ks,uniqueTextMutations:hn},Symbol.toStringTag,{value:"Module"}));var ui="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",mn=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var de=0;de> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`;typeof self<"u"&&self.Blob&&new Blob([gn],{type:"text/javascript;charset=utf-8"});try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((tr=e.contentWindow)==null?void 0:tr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}ns();var fi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(fi||(fi={}));class X{constructor(o){le(this,"fileName"),le(this,"functionName"),le(this,"lineNumber"),le(this,"columnNumber"),this.fileName=o.fileName||"",this.functionName=o.functionName||"",this.lineNumber=o.lineNumber,this.columnNumber=o.columnNumber}toString(){const o=this.lineNumber||"",p=this.columnNumber||"";return this.functionName?`${this.functionName} (${this.fileName}:${o}:${p})`:`${this.fileName}:${o}:${p}`}}const yn=/(^|@)\S+:\d+/,hi=/^\s*at .*(\S+:\d+|\(native\))/m,wn=/^(eval@)?(\[native code])?$/,$t={parse:function(e){return e?typeof e.stacktrace<"u"||typeof e["opera#sourceloc"]<"u"?this.parseOpera(e):e.stack&&e.stack.match(hi)?this.parseV8OrIE(e):e.stack?this.parseFFOrSafari(e):(console.warn("[console-record-plugin]: Failed to parse error object:",e),[]):[]},extractLocation:function(e){if(e.indexOf(":")===-1)return[e];const p=/(.+?)(?::(\d+))?(?::(\d+))?$/.exec(e.replace(/[()]/g,""));if(!p)throw new Error(`Cannot parse given url: ${e}`);return[p[1],p[2]||void 0,p[3]||void 0]},parseV8OrIE:function(e){return e.stack.split(` -`).filter(function(p){return!!p.match(hi)},this).map(function(p){p.indexOf("(eval ")>-1&&(p=p.replace(/eval code/g,"eval").replace(/(\(eval at [^()]*)|(\),.*$)/g,""));let d=p.replace(/^\s+/,"").replace(/\(eval code/g,"(");const s=d.match(/ (\((.+):(\d+):(\d+)\)$)/);d=s?d.replace(s[0],""):d;const f=d.split(/\s+/).slice(1),a=this.extractLocation(s?s[1]:f.pop()),m=f.join(" ")||void 0,n=["eval",""].indexOf(a[0])>-1?void 0:a[0];return new X({functionName:m,fileName:n,lineNumber:a[1],columnNumber:a[2]})},this)},parseFFOrSafari:function(e){return e.stack.split(` -`).filter(function(p){return!p.match(wn)},this).map(function(p){if(p.indexOf(" > eval")>-1&&(p=p.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,":$1")),p.indexOf("@")===-1&&p.indexOf(":")===-1)return new X({functionName:p});{const d=/((.*".+"[^@]*)?[^@]*)(?:@)/,s=p.match(d),f=s&&s[1]?s[1]:void 0,a=this.extractLocation(p.replace(d,""));return new X({functionName:f,fileName:a[0],lineNumber:a[1],columnNumber:a[2]})}},this)},parseOpera:function(e){return!e.stacktrace||e.message.indexOf(` -`)>-1&&e.message.split(` -`).length>e.stacktrace.split(` -`).length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(e){const o=/Line (\d+).*script (?:in )?(\S+)/i,p=e.message.split(` -`),d=[];for(let s=2,f=p.length;s/,"$2").replace(/\([^)]*\)/g,"")||void 0;return new X({functionName:a,fileName:s[0],lineNumber:s[1],columnNumber:s[2]})},this)}};function bn(e){if(!e||!e.outerHTML)return"";let o="";for(;e.parentElement;){let p=e.localName;if(!p)break;p=p.toLowerCase();const d=e.parentElement,s=[];if(d.children&&d.children.length>0)for(let f=0;f1&&(p+=`:eq(${s.indexOf(e)})`),o=p+(o?">"+o:""),e=d}return o}function Dt(e){return Object.prototype.toString.call(e)==="[object Object]"}function Di(e,o){if(o===0)return!0;const p=Object.keys(e);for(const d of p)if(Dt(e[d])&&Di(e[d],o-1))return!0;return!1}function Z(e,o){const p={numOfKeysLimit:50,depthOfLimit:4};Object.assign(p,o);const d=[],s=[];return JSON.stringify(e,function(m,n){if(d.length>0){const h=d.indexOf(this);~h?d.splice(h+1):d.push(this),~h?s.splice(h,1/0,m):s.push(m),~d.indexOf(n)&&(d[0]===n?n="[Circular ~]":n="[Circular ~."+s.slice(0,d.indexOf(n)).join(".")+"]")}else d.push(n);if(n===null)return n;if(n===void 0)return"undefined";if(f(n))return a(n);if(typeof n=="bigint")return n.toString()+"n";if(n instanceof Event){const h={};for(const t in n){const l=n[t];Array.isArray(l)?h[t]=bn(l.length?l[0]:null):h[t]=l}return h}else{if(n instanceof Node)return n instanceof HTMLElement?n?n.outerHTML:"":n.nodeName;if(n instanceof Error)return n.stack?n.stack+` -End of stack for Error object`:n.name+": "+n.message}return n});function f(m){return!!(Dt(m)&&Object.keys(m).length>p.numOfKeysLimit||typeof m=="function"||Dt(m)&&Di(m,p.depthOfLimit))}function a(m){let n=m.toString();return p.stringLengthLimit&&n.length>p.stringLengthLimit&&(n=`${n.slice(0,p.stringLengthLimit)}...`),n}}const ci={level:["assert","clear","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],lengthThreshold:1e3,logger:"console"};function vn(e,o,p){const d=p?Object.assign({},ci,p):ci,s=d.logger;if(!s)return()=>{};let f;typeof s=="string"?f=o[s]:f=s;let a=0,m=!1;const n=[];if(d.level.includes("error")){const t=i=>{const r=i.message,c=i.error,u=$t.parse(c).map(S=>S.toString()),g=[Z(r,d.stringifyOptions)];e({level:"error",trace:u,payload:g})};o.addEventListener("error",t),n.push(()=>{o.removeEventListener("error",t)});const l=i=>{let r,c;i.reason instanceof Error?(r=i.reason,c=[Z(`Uncaught (in promise) ${r.name}: ${r.message}`,d.stringifyOptions)]):(r=new Error,c=[Z("Uncaught (in promise)",d.stringifyOptions),Z(i.reason,d.stringifyOptions)]);const u=$t.parse(r).map(g=>g.toString());e({level:"error",trace:u,payload:c})};o.addEventListener("unhandledrejection",l),n.push(()=>{o.removeEventListener("unhandledrejection",l)})}for(const t of d.level)n.push(h(f,t));return()=>{n.forEach(t=>t())};function h(t,l){return t[l]?dn.patch(t,l,i=>(...r)=>{if(i.apply(this,r),!(l==="assert"&&r[0])&&!m){m=!0;try{const c=$t.parse(new Error).map(S=>S.toString()).splice(1),g=(l==="assert"?r.slice(1):r).map(S=>Z(S,d.stringifyOptions));a++,a{}}}const xn="rrweb/console@1",Sn=e=>({name:xn,observer:vn,options:e});export{xn as PLUGIN_NAME,Sn as getRecordConsolePlugin}; - -``` - ---- - -## .output/public/_nuxt/z593H3Qg.js - -```js -import{_ as s,u as a,o as i,c as u,a as e,t as o}from"./BMw6m4EL.js";const l={class:"antialiased bg-white dark:bg-black dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-black"},c={class:"max-w-520px text-center"},d=["textContent"],p=["textContent"],f={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},version:{type:String,default:""},status:{type:Number,default:500},statusText:{type:String,default:"Server error"},description:{type:String,default:"This page is temporarily unavailable."}},setup(t){const n=t;return a({title:`${n.status} - ${n.statusText} | ${n.appName}`,script:[{innerHTML:`!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`}],style:[{innerHTML:'*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1{font-size:inherit;font-weight:inherit}h1,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }'}]}),(g,r)=>(i(),u("div",l,[r[0]||(r[0]=e("div",{class:"-bottom-1/2 fixed h-1/2 left-0 right-0 spotlight"},null,-1)),e("div",c,[e("h1",{class:"font-medium mb-8 sm:text-10xl text-8xl",textContent:o(t.status)},null,8,d),e("p",{class:"font-light leading-tight mb-16 px-8 sm:px-0 sm:text-4xl text-xl",textContent:o(t.description)},null,8,p)])]))}},b=s(f,[["__scopeId","data-v-9469b13c"]]);export{b as default}; - -``` - ---- - -## .output/public/robots.txt - -```txt -User-Agent: * -Disallow: - -``` - ---- - -## .output/server/chunks/_/error-500.mjs - -```mjs -import { escapeHtml } from '@vue/shared'; - -const _messages = { - "appName": "Nuxt", - "version": "", - "status": 500, - "statusText": "Server error", - "description": "This page is temporarily unavailable." -}; -const template = (messages) => { - messages = { - ..._messages, - ...messages - }; - return "" + escapeHtml(messages.status) + " - " + escapeHtml(messages.statusText) + " | " + escapeHtml(messages.appName) + " - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-nuxt-3.6/references/browser-sdk-2.md b/skills/integration/integration-nuxt-3.6/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-nuxt-3.6/references/browser-sdk-2.md +++ b/skills/integration/integration-nuxt-3.6/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-nuxt-3.6/references/browser-unified-sdk.md b/skills/integration/integration-nuxt-3.6/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-nuxt-3.6/references/browser-unified-sdk.md +++ b/skills/integration/integration-nuxt-3.6/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-nuxt-4/SKILL.md b/skills/integration/integration-nuxt-4/SKILL.md index 3a451b2d7..bedf8409a 100644 --- a/skills/integration/integration-nuxt-4/SKILL.md +++ b/skills/integration/integration-nuxt-4/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Nuxt 4 applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Nuxt 4 example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-nuxt-4/references/EXAMPLE.md b/skills/integration/integration-nuxt-4/references/EXAMPLE.md index 665984ef3..749399c12 100644 --- a/skills/integration/integration-nuxt-4/references/EXAMPLE.md +++ b/skills/integration/integration-nuxt-4/references/EXAMPLE.md @@ -11,7 +11,7 @@ Path: basics/nuxt-4 This is a [Nuxt 4](https://nuxt.com) example demonstrating Amplitude integration with product analytics and event tracking. -For Nuxt 3.0 - 3.6, see the [Nuxt 3.6 example](../nuxt-3.6) for an alternative approach. +For Nuxt 3.0 - 3.6, see the [Nuxt 3.6 example](../../integration-nuxt-3.6/) for an alternative approach. ### Amplitude SDKs @@ -221,7778 +221,6 @@ NUXT_PUBLIC_AMPLITUDE_API_KEY= --- -## .nuxt/app.config.mjs - -```mjs - -import { defuFn } from 'defu' - -const inlineConfig = { - "nuxt": {} -} - -/** client **/ -import { _replaceAppConfig } from '#app/config' - -// Vite - webpack is handled directly in #app/config -if (import.meta.dev && !import.meta.nitro && import.meta.hot) { - import.meta.hot.accept((newModule) => { - _replaceAppConfig(newModule.default) - }) -} -/** client-end **/ - - - -export default /*@__PURE__*/ defuFn(inlineConfig) - -``` - ---- - -## .nuxt/components.d.ts - -```ts - -import type { DefineComponent, SlotsType } from 'vue' -type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T - -type HydrationStrategies = { - hydrateOnVisible?: IntersectionObserverInit | true - hydrateOnIdle?: number | true - hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true - hydrateOnMediaQuery?: string - hydrateAfter?: number - hydrateWhen?: boolean - hydrateNever?: true -} -type LazyComponent = DefineComponent void }> & T - - -export const AppHeader: typeof import("../app/components/AppHeader.vue").default -export const NuxtWelcome: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/welcome.vue").default -export const NuxtLayout: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-layout").default -export const NuxtErrorBoundary: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue").default -export const ClientOnly: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/client-only").default -export const DevOnly: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/dev-only").default -export const ServerPlaceholder: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/server-placeholder").default -export const NuxtLink: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-link").default -export const NuxtLoadingIndicator: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-loading-indicator").default -export const NuxtTime: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-time.vue").default -export const NuxtRouteAnnouncer: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-route-announcer").default -export const NuxtImg: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtImg -export const NuxtPicture: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtPicture -export const NuxtPage: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/pages/runtime/page").default -export const NoScript: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").NoScript -export const Link: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Link -export const Base: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Base -export const Title: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Title -export const Meta: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Meta -export const Style: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Style -export const Head: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Head -export const Html: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Html -export const Body: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Body -export const NuxtIsland: typeof import("../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-island").default -export const LazyAppHeader: LazyComponent -export const LazyNuxtWelcome: LazyComponent -export const LazyNuxtLayout: LazyComponent -export const LazyNuxtErrorBoundary: LazyComponent -export const LazyClientOnly: LazyComponent -export const LazyDevOnly: LazyComponent -export const LazyServerPlaceholder: LazyComponent -export const LazyNuxtLink: LazyComponent -export const LazyNuxtLoadingIndicator: LazyComponent -export const LazyNuxtTime: LazyComponent -export const LazyNuxtRouteAnnouncer: LazyComponent -export const LazyNuxtImg: LazyComponent -export const LazyNuxtPicture: LazyComponent -export const LazyNuxtPage: LazyComponent -export const LazyNoScript: LazyComponent -export const LazyLink: LazyComponent -export const LazyBase: LazyComponent -export const LazyTitle: LazyComponent -export const LazyMeta: LazyComponent -export const LazyStyle: LazyComponent -export const LazyHead: LazyComponent -export const LazyHtml: LazyComponent -export const LazyBody: LazyComponent -export const LazyNuxtIsland: LazyComponent - -export const componentNames: string[] - -``` - ---- - -## .nuxt/imports.d.ts - -```ts -export { useScriptTriggerConsent, useScriptEventPage, useScriptTriggerElement, useScript, useScriptGoogleAnalytics, useScriptPlausibleAnalytics, useScriptCrisp, useScriptClarity, useScriptCloudflareWebAnalytics, useScriptFathomAnalytics, useScriptMatomoAnalytics, useScriptGoogleTagManager, useScriptGoogleAdsense, useScriptSegment, useScriptMetaPixel, useScriptXPixel, useScriptIntercom, useScriptHotjar, useScriptStripe, useScriptLemonSqueezy, useScriptVimeoPlayer, useScriptYouTubePlayer, useScriptGoogleMaps, useScriptNpm, useScriptUmamiAnalytics, useScriptSnapchatPixel, useScriptRybbitAnalytics, useScriptDatabuddyAnalytics, useScriptRedditPixel, useScriptPayPal } from '#app/composables/script-stubs'; -export { isVue2, isVue3 } from 'vue-demi'; -export { defineNuxtLink } from '#app/components/nuxt-link'; -export { useNuxtApp, tryUseNuxtApp, defineNuxtPlugin, definePayloadPlugin, useRuntimeConfig, defineAppConfig } from '#app/nuxt'; -export { useAppConfig, updateAppConfig } from '#app/config'; -export { defineNuxtComponent } from '#app/composables/component'; -export { useAsyncData, useLazyAsyncData, useNuxtData, refreshNuxtData, clearNuxtData } from '#app/composables/asyncData'; -export { useHydration } from '#app/composables/hydrate'; -export { callOnce } from '#app/composables/once'; -export { useState, clearNuxtState } from '#app/composables/state'; -export { clearError, createError, isNuxtError, showError, useError } from '#app/composables/error'; -export { useFetch, useLazyFetch } from '#app/composables/fetch'; -export { useCookie, refreshCookie } from '#app/composables/cookie'; -export { onPrehydrate, prerenderRoutes, useRequestHeader, useRequestHeaders, useResponseHeader, useRequestEvent, useRequestFetch, setResponseStatus } from '#app/composables/ssr'; -export { onNuxtReady } from '#app/composables/ready'; -export { preloadComponents, prefetchComponents, preloadRouteComponents } from '#app/composables/preload'; -export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter } from '#app/composables/router'; -export { isPrerendered, loadPayload, preloadPayload, definePayloadReducer, definePayloadReviver } from '#app/composables/payload'; -export { useLoadingIndicator } from '#app/composables/loading-indicator'; -export { getAppManifest, getRouteRules } from '#app/composables/manifest'; -export { reloadNuxtApp } from '#app/composables/chunk'; -export { useRequestURL } from '#app/composables/url'; -export { usePreviewMode } from '#app/composables/preview'; -export { useRouteAnnouncer } from '#app/composables/route-announcer'; -export { useRuntimeHook } from '#app/composables/runtime-hook'; -export { useHead, useHeadSafe, useServerHeadSafe, useServerHead, useSeoMeta, useServerSeoMeta, injectHead } from '#app/composables/head'; -export { onBeforeRouteLeave, onBeforeRouteUpdate, useLink } from 'vue-router'; -export { withCtx, withDirectives, withKeys, withMemo, withModifiers, withScopeId, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onServerPrefetch, onUnmounted, onUpdated, computed, customRef, isProxy, isReactive, isReadonly, isRef, markRaw, proxyRefs, reactive, readonly, ref, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, watch, watchEffect, watchPostEffect, watchSyncEffect, onWatcherCleanup, isShallow, effect, effectScope, getCurrentScope, onScopeDispose, defineComponent, defineAsyncComponent, resolveComponent, getCurrentInstance, h, inject, hasInjectionContext, nextTick, provide, toValue, useModel, useAttrs, useCssModule, useCssVars, useSlots, useTransitionState, useId, useTemplateRef, useShadowRoot, Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'; -export { requestIdleCallback, cancelIdleCallback } from '#app/compat/idle-callback'; -export { setInterval } from '#app/compat/interval'; -export { definePageMeta } from '../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/pages/runtime/composables'; -export { defineLazyHydrationComponent } from '#app/composables/lazy-hydration'; -export { useAmplitude } from '../app/composables/useAmplitude'; -export { useAuth } from '../app/composables/useAuth'; -export { loginSchema, validateForm, LoginFormData } from '../app/utils/formValidation'; -``` - ---- - -## .nuxt/nuxt.d.ts - -```ts -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// - -export {} - -``` - ---- - -## .nuxt/nuxt.node.d.ts - -```ts -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// - -export {} - -``` - ---- - -## .nuxt/nuxt.shared.d.ts - -```ts -/// -/// -/// -/// - -export {} - -``` - ---- - -## .nuxt/schema/nuxt.schema.d.ts - -```ts -export interface NuxtCustomSchema { - -} -export type CustomAppConfig = Exclude -type _CustomAppConfig = CustomAppConfig - -declare module '@nuxt/schema' { - interface NuxtConfig extends Omit {} - interface NuxtOptions extends Omit {} - interface CustomAppConfig extends _CustomAppConfig {} -} - -declare module 'nuxt/schema' { - interface NuxtConfig extends Omit {} - interface NuxtOptions extends Omit {} - interface CustomAppConfig extends _CustomAppConfig {} -} - -``` - ---- - -## .nuxt/types/app.config.d.ts - -```ts - -import type { AppConfigInput, CustomAppConfig } from 'nuxt/schema' -import type { Defu } from 'defu' - - -declare global { - const defineAppConfig: (config: C) => C -} - -declare const inlineConfig = { - "nuxt": {} -} -type ResolvedAppConfig = Defu -type IsAny = 0 extends 1 & T ? true : false - -type MergedAppConfig, Custom extends Record> = { - [K in keyof (Resolved & Custom)]: K extends keyof Custom - ? unknown extends Custom[K] - ? Resolved[K] - : IsAny extends true - ? Resolved[K] - : Custom[K] extends Record - ? Resolved[K] extends Record - ? MergedAppConfig - : Exclude - : Exclude - : Resolved[K] -} - -declare module 'nuxt/schema' { - interface AppConfig extends MergedAppConfig { } -} -declare module '@nuxt/schema' { - interface AppConfig extends MergedAppConfig { } -} - -``` - ---- - -## .nuxt/types/components.d.ts - -```ts - -import type { DefineComponent, SlotsType } from 'vue' -type IslandComponent = DefineComponent<{}, {refresh: () => Promise}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>> & T - -type HydrationStrategies = { - hydrateOnVisible?: IntersectionObserverInit | true - hydrateOnIdle?: number | true - hydrateOnInteraction?: keyof HTMLElementEventMap | Array | true - hydrateOnMediaQuery?: string - hydrateAfter?: number - hydrateWhen?: boolean - hydrateNever?: true -} -type LazyComponent = DefineComponent void }> & T - -interface _GlobalComponents { - 'AppHeader': typeof import("../../app/components/AppHeader.vue").default - 'NuxtWelcome': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/welcome.vue").default - 'NuxtLayout': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-layout").default - 'NuxtErrorBoundary': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-error-boundary.vue").default - 'ClientOnly': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/client-only").default - 'DevOnly': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/dev-only").default - 'ServerPlaceholder': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/server-placeholder").default - 'NuxtLink': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-link").default - 'NuxtLoadingIndicator': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-loading-indicator").default - 'NuxtTime': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-time.vue").default - 'NuxtRouteAnnouncer': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-route-announcer").default - 'NuxtImg': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtImg - 'NuxtPicture': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-stubs").NuxtPicture - 'NuxtPage': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/pages/runtime/page").default - 'NoScript': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").NoScript - 'Link': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Link - 'Base': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Base - 'Title': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Title - 'Meta': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Meta - 'Style': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Style - 'Head': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Head - 'Html': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Html - 'Body': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/head/runtime/components").Body - 'NuxtIsland': typeof import("../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-island").default - 'LazyAppHeader': LazyComponent - 'LazyNuxtWelcome': LazyComponent - 'LazyNuxtLayout': LazyComponent - 'LazyNuxtErrorBoundary': LazyComponent - 'LazyClientOnly': LazyComponent - 'LazyDevOnly': LazyComponent - 'LazyServerPlaceholder': LazyComponent - 'LazyNuxtLink': LazyComponent - 'LazyNuxtLoadingIndicator': LazyComponent - 'LazyNuxtTime': LazyComponent - 'LazyNuxtRouteAnnouncer': LazyComponent - 'LazyNuxtImg': LazyComponent - 'LazyNuxtPicture': LazyComponent - 'LazyNuxtPage': LazyComponent - 'LazyNoScript': LazyComponent - 'LazyLink': LazyComponent - 'LazyBase': LazyComponent - 'LazyTitle': LazyComponent - 'LazyMeta': LazyComponent - 'LazyStyle': LazyComponent - 'LazyHead': LazyComponent - 'LazyHtml': LazyComponent - 'LazyBody': LazyComponent - 'LazyNuxtIsland': LazyComponent -} - -declare module 'vue' { - export interface GlobalComponents extends _GlobalComponents { } -} - -export {} - -``` - ---- - -## .nuxt/types/imports.d.ts - -```ts -// Generated by auto imports -export {} -declare global { - const abortNavigation: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').abortNavigation - const addRouteMiddleware: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').addRouteMiddleware - const callOnce: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/once').callOnce - const cancelIdleCallback: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/compat/idle-callback').cancelIdleCallback - const clearError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').clearError - const clearNuxtData: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/asyncData').clearNuxtData - const clearNuxtState: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/state').clearNuxtState - const computed: typeof import('vue').computed - const createError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').createError - const customRef: typeof import('vue').customRef - const defineAppConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').defineAppConfig - const defineAsyncComponent: typeof import('vue').defineAsyncComponent - const defineComponent: typeof import('vue').defineComponent - const defineLazyHydrationComponent: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/lazy-hydration').defineLazyHydrationComponent - const defineNuxtComponent: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/component').defineNuxtComponent - const defineNuxtLink: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/components/nuxt-link').defineNuxtLink - const defineNuxtPlugin: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').defineNuxtPlugin - const defineNuxtRouteMiddleware: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').defineNuxtRouteMiddleware - const definePageMeta: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/pages/runtime/composables').definePageMeta - const definePayloadPlugin: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').definePayloadPlugin - const definePayloadReducer: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/payload').definePayloadReducer - const definePayloadReviver: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/payload').definePayloadReviver - const effect: typeof import('vue').effect - const effectScope: typeof import('vue').effectScope - const getAppManifest: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/manifest').getAppManifest - const getCurrentInstance: typeof import('vue').getCurrentInstance - const getCurrentScope: typeof import('vue').getCurrentScope - const getRouteRules: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/manifest').getRouteRules - const h: typeof import('vue').h - const hasInjectionContext: typeof import('vue').hasInjectionContext - const inject: typeof import('vue').inject - const injectHead: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').injectHead - const isNuxtError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').isNuxtError - const isPrerendered: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/payload').isPrerendered - const isProxy: typeof import('vue').isProxy - const isReactive: typeof import('vue').isReactive - const isReadonly: typeof import('vue').isReadonly - const isRef: typeof import('vue').isRef - const isShallow: typeof import('vue').isShallow - const isVue2: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/compat/vue-demi').isVue2 - const isVue3: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/compat/vue-demi').isVue3 - const loadPayload: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/payload').loadPayload - const loginSchema: typeof import('../../app/utils/formValidation').loginSchema - const markRaw: typeof import('vue').markRaw - const navigateTo: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').navigateTo - const nextTick: typeof import('vue').nextTick - const onActivated: typeof import('vue').onActivated - const onBeforeMount: typeof import('vue').onBeforeMount - const onBeforeRouteLeave: typeof import('vue-router').onBeforeRouteLeave - const onBeforeRouteUpdate: typeof import('vue-router').onBeforeRouteUpdate - const onBeforeUnmount: typeof import('vue').onBeforeUnmount - const onBeforeUpdate: typeof import('vue').onBeforeUpdate - const onDeactivated: typeof import('vue').onDeactivated - const onErrorCaptured: typeof import('vue').onErrorCaptured - const onMounted: typeof import('vue').onMounted - const onNuxtReady: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ready').onNuxtReady - const onPrehydrate: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').onPrehydrate - const onRenderTracked: typeof import('vue').onRenderTracked - const onRenderTriggered: typeof import('vue').onRenderTriggered - const onScopeDispose: typeof import('vue').onScopeDispose - const onServerPrefetch: typeof import('vue').onServerPrefetch - const onUnmounted: typeof import('vue').onUnmounted - const onUpdated: typeof import('vue').onUpdated - const onWatcherCleanup: typeof import('vue').onWatcherCleanup - const prefetchComponents: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/preload').prefetchComponents - const preloadComponents: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/preload').preloadComponents - const preloadPayload: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/payload').preloadPayload - const preloadRouteComponents: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/preload').preloadRouteComponents - const prerenderRoutes: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').prerenderRoutes - const provide: typeof import('vue').provide - const proxyRefs: typeof import('vue').proxyRefs - const reactive: typeof import('vue').reactive - const readonly: typeof import('vue').readonly - const ref: typeof import('vue').ref - const refreshCookie: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/cookie').refreshCookie - const refreshNuxtData: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/asyncData').refreshNuxtData - const reloadNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/chunk').reloadNuxtApp - const requestIdleCallback: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/compat/idle-callback').requestIdleCallback - const resolveComponent: typeof import('vue').resolveComponent - const setInterval: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/compat/interval').setInterval - const setPageLayout: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').setPageLayout - const setResponseStatus: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').setResponseStatus - const shallowReactive: typeof import('vue').shallowReactive - const shallowReadonly: typeof import('vue').shallowReadonly - const shallowRef: typeof import('vue').shallowRef - const showError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').showError - const toRaw: typeof import('vue').toRaw - const toRef: typeof import('vue').toRef - const toRefs: typeof import('vue').toRefs - const toValue: typeof import('vue').toValue - const triggerRef: typeof import('vue').triggerRef - const tryUseNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').tryUseNuxtApp - const unref: typeof import('vue').unref - const updateAppConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/config').updateAppConfig - const useAmplitude: typeof import('../../app/composables/useAmplitude').useAmplitude - const useAppConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/config').useAppConfig - const useAsyncData: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/asyncData').useAsyncData - const useAttrs: typeof import('vue').useAttrs - const useAuth: typeof import('../../app/composables/useAuth').useAuth - const useCookie: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/cookie').useCookie - const useCssModule: typeof import('vue').useCssModule - const useCssVars: typeof import('vue').useCssVars - const useError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').useError - const useFetch: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/fetch').useFetch - const useHead: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useHead - const useHeadSafe: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useHeadSafe - const useHydration: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/hydrate').useHydration - const useId: typeof import('vue').useId - const useLazyAsyncData: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/asyncData').useLazyAsyncData - const useLazyFetch: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/fetch').useLazyFetch - const useLink: typeof import('vue-router').useLink - const useLoadingIndicator: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/loading-indicator').useLoadingIndicator - const useModel: typeof import('vue').useModel - const useNuxtApp: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').useNuxtApp - const useNuxtData: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/asyncData').useNuxtData - const usePreviewMode: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/preview').usePreviewMode - const useRequestEvent: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').useRequestEvent - const useRequestFetch: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').useRequestFetch - const useRequestHeader: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').useRequestHeader - const useRequestHeaders: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').useRequestHeaders - const useRequestURL: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/url').useRequestURL - const useResponseHeader: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').useResponseHeader - const useRoute: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').useRoute - const useRouteAnnouncer: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/route-announcer').useRouteAnnouncer - const useRouter: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/router').useRouter - const useRuntimeConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').useRuntimeConfig - const useRuntimeHook: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/runtime-hook').useRuntimeHook - const useScript: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScript - const useScriptClarity: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptClarity - const useScriptCloudflareWebAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptCloudflareWebAnalytics - const useScriptCrisp: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptCrisp - const useScriptDatabuddyAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptDatabuddyAnalytics - const useScriptEventPage: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptEventPage - const useScriptFathomAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptFathomAnalytics - const useScriptGoogleAdsense: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleAdsense - const useScriptGoogleAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleAnalytics - const useScriptGoogleMaps: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleMaps - const useScriptGoogleTagManager: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptGoogleTagManager - const useScriptHotjar: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptHotjar - const useScriptIntercom: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptIntercom - const useScriptLemonSqueezy: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptLemonSqueezy - const useScriptMatomoAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptMatomoAnalytics - const useScriptMetaPixel: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptMetaPixel - const useScriptNpm: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptNpm - const useScriptPayPal: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptPayPal - const useScriptPlausibleAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptPlausibleAnalytics - const useScriptRedditPixel: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptRedditPixel - const useScriptRybbitAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptRybbitAnalytics - const useScriptSegment: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptSegment - const useScriptSnapchatPixel: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptSnapchatPixel - const useScriptStripe: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptStripe - const useScriptTriggerConsent: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptTriggerConsent - const useScriptTriggerElement: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptTriggerElement - const useScriptUmamiAnalytics: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptUmamiAnalytics - const useScriptVimeoPlayer: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptVimeoPlayer - const useScriptXPixel: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptXPixel - const useScriptYouTubePlayer: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/script-stubs').useScriptYouTubePlayer - const useSeoMeta: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useSeoMeta - const useServerHead: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useServerHead - const useServerHeadSafe: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useServerHeadSafe - const useServerSeoMeta: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/head').useServerSeoMeta - const useShadowRoot: typeof import('vue').useShadowRoot - const useSlots: typeof import('vue').useSlots - const useState: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/state').useState - const useTemplateRef: typeof import('vue').useTemplateRef - const useTransitionState: typeof import('vue').useTransitionState - const validateForm: typeof import('../../app/utils/formValidation').validateForm - const watch: typeof import('vue').watch - const watchEffect: typeof import('vue').watchEffect - const watchPostEffect: typeof import('vue').watchPostEffect - const watchSyncEffect: typeof import('vue').watchSyncEffect - const withCtx: typeof import('vue').withCtx - const withDirectives: typeof import('vue').withDirectives - const withKeys: typeof import('vue').withKeys - const withMemo: typeof import('vue').withMemo - const withModifiers: typeof import('vue').withModifiers - const withScopeId: typeof import('vue').withScopeId -} -// for type re-export -declare global { - // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' - import('vue') - // @ts-ignore - export type { LoginFormData } from '../../app/utils/formValidation' - import('../../app/utils/formValidation') -} -// for vue template auto import -import { UnwrapRef } from 'vue' -declare module 'vue' { - interface ComponentCustomProperties { - readonly abortNavigation: UnwrapRef - readonly addRouteMiddleware: UnwrapRef - readonly callOnce: UnwrapRef - readonly cancelIdleCallback: UnwrapRef - readonly clearError: UnwrapRef - readonly clearNuxtData: UnwrapRef - readonly clearNuxtState: UnwrapRef - readonly computed: UnwrapRef - readonly createError: UnwrapRef - readonly customRef: UnwrapRef - readonly defineAppConfig: UnwrapRef - readonly defineAsyncComponent: UnwrapRef - readonly defineComponent: UnwrapRef - readonly defineLazyHydrationComponent: UnwrapRef - readonly defineNuxtComponent: UnwrapRef - readonly defineNuxtLink: UnwrapRef - readonly defineNuxtPlugin: UnwrapRef - readonly defineNuxtRouteMiddleware: UnwrapRef - readonly definePageMeta: UnwrapRef - readonly definePayloadPlugin: UnwrapRef - readonly definePayloadReducer: UnwrapRef - readonly definePayloadReviver: UnwrapRef - readonly effect: UnwrapRef - readonly effectScope: UnwrapRef - readonly getAppManifest: UnwrapRef - readonly getCurrentInstance: UnwrapRef - readonly getCurrentScope: UnwrapRef - readonly getRouteRules: UnwrapRef - readonly h: UnwrapRef - readonly hasInjectionContext: UnwrapRef - readonly inject: UnwrapRef - readonly injectHead: UnwrapRef - readonly isNuxtError: UnwrapRef - readonly isPrerendered: UnwrapRef - readonly isProxy: UnwrapRef - readonly isReactive: UnwrapRef - readonly isReadonly: UnwrapRef - readonly isRef: UnwrapRef - readonly isShallow: UnwrapRef - readonly isVue2: UnwrapRef - readonly isVue3: UnwrapRef - readonly loadPayload: UnwrapRef - readonly loginSchema: UnwrapRef - readonly markRaw: UnwrapRef - readonly navigateTo: UnwrapRef - readonly nextTick: UnwrapRef - readonly onActivated: UnwrapRef - readonly onBeforeMount: UnwrapRef - readonly onBeforeRouteLeave: UnwrapRef - readonly onBeforeRouteUpdate: UnwrapRef - readonly onBeforeUnmount: UnwrapRef - readonly onBeforeUpdate: UnwrapRef - readonly onDeactivated: UnwrapRef - readonly onErrorCaptured: UnwrapRef - readonly onMounted: UnwrapRef - readonly onNuxtReady: UnwrapRef - readonly onPrehydrate: UnwrapRef - readonly onRenderTracked: UnwrapRef - readonly onRenderTriggered: UnwrapRef - readonly onScopeDispose: UnwrapRef - readonly onServerPrefetch: UnwrapRef - readonly onUnmounted: UnwrapRef - readonly onUpdated: UnwrapRef - readonly onWatcherCleanup: UnwrapRef - readonly prefetchComponents: UnwrapRef - readonly preloadComponents: UnwrapRef - readonly preloadPayload: UnwrapRef - readonly preloadRouteComponents: UnwrapRef - readonly prerenderRoutes: UnwrapRef - readonly provide: UnwrapRef - readonly proxyRefs: UnwrapRef - readonly reactive: UnwrapRef - readonly readonly: UnwrapRef - readonly ref: UnwrapRef - readonly refreshCookie: UnwrapRef - readonly refreshNuxtData: UnwrapRef - readonly reloadNuxtApp: UnwrapRef - readonly requestIdleCallback: UnwrapRef - readonly resolveComponent: UnwrapRef - readonly setInterval: UnwrapRef - readonly setPageLayout: UnwrapRef - readonly setResponseStatus: UnwrapRef - readonly shallowReactive: UnwrapRef - readonly shallowReadonly: UnwrapRef - readonly shallowRef: UnwrapRef - readonly showError: UnwrapRef - readonly toRaw: UnwrapRef - readonly toRef: UnwrapRef - readonly toRefs: UnwrapRef - readonly toValue: UnwrapRef - readonly triggerRef: UnwrapRef - readonly tryUseNuxtApp: UnwrapRef - readonly unref: UnwrapRef - readonly updateAppConfig: UnwrapRef - readonly useAmplitude: UnwrapRef - readonly useAppConfig: UnwrapRef - readonly useAsyncData: UnwrapRef - readonly useAttrs: UnwrapRef - readonly useAuth: UnwrapRef - readonly useCookie: UnwrapRef - readonly useCssModule: UnwrapRef - readonly useCssVars: UnwrapRef - readonly useError: UnwrapRef - readonly useFetch: UnwrapRef - readonly useHead: UnwrapRef - readonly useHeadSafe: UnwrapRef - readonly useHydration: UnwrapRef - readonly useId: UnwrapRef - readonly useLazyAsyncData: UnwrapRef - readonly useLazyFetch: UnwrapRef - readonly useLink: UnwrapRef - readonly useLoadingIndicator: UnwrapRef - readonly useModel: UnwrapRef - readonly useNuxtApp: UnwrapRef - readonly useNuxtData: UnwrapRef - readonly usePreviewMode: UnwrapRef - readonly useRequestEvent: UnwrapRef - readonly useRequestFetch: UnwrapRef - readonly useRequestHeader: UnwrapRef - readonly useRequestHeaders: UnwrapRef - readonly useRequestURL: UnwrapRef - readonly useResponseHeader: UnwrapRef - readonly useRoute: UnwrapRef - readonly useRouteAnnouncer: UnwrapRef - readonly useRouter: UnwrapRef - readonly useRuntimeConfig: UnwrapRef - readonly useRuntimeHook: UnwrapRef - readonly useScript: UnwrapRef - readonly useScriptClarity: UnwrapRef - readonly useScriptCloudflareWebAnalytics: UnwrapRef - readonly useScriptCrisp: UnwrapRef - readonly useScriptDatabuddyAnalytics: UnwrapRef - readonly useScriptEventPage: UnwrapRef - readonly useScriptFathomAnalytics: UnwrapRef - readonly useScriptGoogleAdsense: UnwrapRef - readonly useScriptGoogleAnalytics: UnwrapRef - readonly useScriptGoogleMaps: UnwrapRef - readonly useScriptGoogleTagManager: UnwrapRef - readonly useScriptHotjar: UnwrapRef - readonly useScriptIntercom: UnwrapRef - readonly useScriptLemonSqueezy: UnwrapRef - readonly useScriptMatomoAnalytics: UnwrapRef - readonly useScriptMetaPixel: UnwrapRef - readonly useScriptNpm: UnwrapRef - readonly useScriptPayPal: UnwrapRef - readonly useScriptPlausibleAnalytics: UnwrapRef - readonly useScriptRedditPixel: UnwrapRef - readonly useScriptRybbitAnalytics: UnwrapRef - readonly useScriptSegment: UnwrapRef - readonly useScriptSnapchatPixel: UnwrapRef - readonly useScriptStripe: UnwrapRef - readonly useScriptTriggerConsent: UnwrapRef - readonly useScriptTriggerElement: UnwrapRef - readonly useScriptUmamiAnalytics: UnwrapRef - readonly useScriptVimeoPlayer: UnwrapRef - readonly useScriptXPixel: UnwrapRef - readonly useScriptYouTubePlayer: UnwrapRef - readonly useSeoMeta: UnwrapRef - readonly useServerHead: UnwrapRef - readonly useServerHeadSafe: UnwrapRef - readonly useServerSeoMeta: UnwrapRef - readonly useShadowRoot: UnwrapRef - readonly useSlots: UnwrapRef - readonly useState: UnwrapRef - readonly useTemplateRef: UnwrapRef - readonly useTransitionState: UnwrapRef - readonly validateForm: UnwrapRef - readonly watch: UnwrapRef - readonly watchEffect: UnwrapRef - readonly watchPostEffect: UnwrapRef - readonly watchSyncEffect: UnwrapRef - readonly withCtx: UnwrapRef - readonly withDirectives: UnwrapRef - readonly withKeys: UnwrapRef - readonly withMemo: UnwrapRef - readonly withModifiers: UnwrapRef - readonly withScopeId: UnwrapRef - } -} -``` - ---- - -## .nuxt/types/layouts.d.ts - -```ts -import type { ComputedRef, MaybeRef } from 'vue' -declare module 'nuxt/app' { - interface NuxtLayouts { -} - export type LayoutKey = keyof NuxtLayouts extends never ? string : keyof NuxtLayouts - interface PageMeta { - layout?: MaybeRef | ComputedRef - } -} -``` - ---- - -## .nuxt/types/middleware.d.ts - -```ts -import type { NavigationGuard } from 'vue-router' -export type MiddlewareKey = "auth" -declare module 'nuxt/app' { - interface PageMeta { - middleware?: MiddlewareKey | NavigationGuard | Array - } -} -``` - ---- - -## .nuxt/types/modules.d.ts - -```ts -import { NuxtModule, ModuleDependencyMeta } from '@nuxt/schema' -declare module '@nuxt/schema' { - interface ModuleDependencies { - ["@nuxt/devtools"]?: ModuleDependencyMeta ? O | false : Record> | false - ["@nuxt/telemetry"]?: ModuleDependencyMeta ? O | false : Record> | false - } - interface NuxtOptions { - /** - * Configuration for `@nuxt/devtools` - */ - ["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule ? O | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - */ - ["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule ? O | false : Record | false - } - interface NuxtConfig { - /** - * Configuration for `@nuxt/devtools` - */ - ["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule ? Partial | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - */ - ["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule ? Partial | false : Record | false - modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ["@nuxt/devtools", Exclude] | ["@nuxt/telemetry", Exclude])[], - } -} -declare module 'nuxt/schema' { - interface ModuleDependencies { - ["@nuxt/devtools"]?: ModuleDependencyMeta ? O | false : Record> | false - ["@nuxt/telemetry"]?: ModuleDependencyMeta ? O | false : Record> | false - } - interface NuxtOptions { - /** - * Configuration for `@nuxt/devtools` - * @see https://www.npmjs.com/package/@nuxt/devtools - */ - ["devtools"]: typeof import("@nuxt/devtools").default extends NuxtModule ? O | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - * @see https://www.npmjs.com/package/@nuxt/telemetry - */ - ["telemetry"]: typeof import("@nuxt/telemetry").default extends NuxtModule ? O | false : Record | false - } - interface NuxtConfig { - /** - * Configuration for `@nuxt/devtools` - * @see https://www.npmjs.com/package/@nuxt/devtools - */ - ["devtools"]?: typeof import("@nuxt/devtools").default extends NuxtModule ? Partial | false : Record | false - /** - * Configuration for `@nuxt/telemetry` - * @see https://www.npmjs.com/package/@nuxt/telemetry - */ - ["telemetry"]?: typeof import("@nuxt/telemetry").default extends NuxtModule ? Partial | false : Record | false - modules?: (undefined | null | false | NuxtModule | string | [NuxtModule | string, Record] | ["@nuxt/devtools", Exclude] | ["@nuxt/telemetry", Exclude])[], - } -} -``` - ---- - -## .nuxt/types/nitro-config.d.ts - -```ts -// Generated by nitro - -// App Config -import type { Defu } from 'defu' - - - -type UserAppConfig = Defu<{}, []> - -declare module "nitropack/types" { - interface AppConfig extends UserAppConfig {} - -} -export {} -``` - ---- - -## .nuxt/types/nitro-imports.d.ts - -```ts -declare global { - const H3Error: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').H3Error - const H3Event: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').H3Event - const __buildAssetsURL: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@4.3.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@4_e7e8ae81c59162932a5ca46c47340ed2/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths').buildAssetsURL - const __publicAssetsURL: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@4.3.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@4_e7e8ae81c59162932a5ca46c47340ed2/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths').publicAssetsURL - const appendCorsHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendCorsHeaders - const appendCorsPreflightHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendCorsPreflightHeaders - const appendHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendHeader - const appendHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendHeaders - const appendResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendResponseHeader - const appendResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').appendResponseHeaders - const assertMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').assertMethod - const cachedEventHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').cachedEventHandler - const cachedFunction: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').cachedFunction - const callNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').callNodeListener - const clearResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').clearResponseHeaders - const clearSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').clearSession - const createApp: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createApp - const createAppEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createAppEventHandler - const createError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createError - const createEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createEvent - const createEventStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createEventStream - const createRouter: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').createRouter - const defaultContentType: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defaultContentType - const defineAppConfig: typeof import('../../node_modules/.pnpm/@nuxt+nitro-server@4.3.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@4_e7e8ae81c59162932a5ca46c47340ed2/node_modules/@nuxt/nitro-server/dist/runtime/utils/config').defineAppConfig - const defineCachedEventHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').defineCachedEventHandler - const defineCachedFunction: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/cache').defineCachedFunction - const defineEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineEventHandler - const defineLazyEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineLazyEventHandler - const defineNitroErrorHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/error/utils').defineNitroErrorHandler - const defineNitroPlugin: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/plugin').defineNitroPlugin - const defineNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineNodeListener - const defineNodeMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineNodeMiddleware - const defineRenderHandler: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/renderer').defineRenderHandler - const defineRequestMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineRequestMiddleware - const defineResponseMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineResponseMiddleware - const defineRouteMeta: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/meta').defineRouteMeta - const defineTask: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/task').defineTask - const defineWebSocket: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineWebSocket - const defineWebSocketHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').defineWebSocketHandler - const deleteCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').deleteCookie - const dynamicEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').dynamicEventHandler - const eventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').eventHandler - const fetchWithEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fetchWithEvent - const fromNodeMiddleware: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromNodeMiddleware - const fromPlainHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromPlainHandler - const fromWebHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').fromWebHandler - const getCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getCookie - const getHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getHeader - const getHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getHeaders - const getMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getMethod - const getOrCreateUser: typeof import('../../server/utils/users').getOrCreateUser - const getProxyRequestHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getProxyRequestHeaders - const getQuery: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getQuery - const getRequestFingerprint: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestFingerprint - const getRequestHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHeader - const getRequestHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHeaders - const getRequestHost: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestHost - const getRequestIP: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestIP - const getRequestPath: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestPath - const getRequestProtocol: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestProtocol - const getRequestURL: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestURL - const getRequestWebStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRequestWebStream - const getResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseHeader - const getResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseHeaders - const getResponseStatus: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseStatus - const getResponseStatusText: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getResponseStatusText - const getRouteRules: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/route-rules').getRouteRules - const getRouterParam: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRouterParam - const getRouterParams: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getRouterParams - const getSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getSession - const getValidatedQuery: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getValidatedQuery - const getValidatedRouterParams: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').getValidatedRouterParams - const handleCacheHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').handleCacheHeaders - const handleCors: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').handleCors - const incrementBurritoConsiderations: typeof import('../../server/utils/users').incrementBurritoConsiderations - const isCorsOriginAllowed: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isCorsOriginAllowed - const isError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isError - const isEvent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isEvent - const isEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isEventHandler - const isMethod: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isMethod - const isPreflightRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isPreflightRequest - const isStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isStream - const isWebResponse: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').isWebResponse - const lazyEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').lazyEventHandler - const nitroPlugin: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/plugin').nitroPlugin - const parseCookies: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').parseCookies - const promisifyNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').promisifyNodeListener - const proxyRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').proxyRequest - const readBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readBody - const readFormData: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readFormData - const readMultipartFormData: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readMultipartFormData - const readRawBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readRawBody - const readValidatedBody: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').readValidatedBody - const removeResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').removeResponseHeader - const runTask: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/task').runTask - const sanitizeStatusCode: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sanitizeStatusCode - const sanitizeStatusMessage: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sanitizeStatusMessage - const sealSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sealSession - const send: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').send - const sendError: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendError - const sendIterable: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendIterable - const sendNoContent: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendNoContent - const sendProxy: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendProxy - const sendRedirect: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendRedirect - const sendStream: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendStream - const sendWebResponse: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').sendWebResponse - const serveStatic: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').serveStatic - const setCookie: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setCookie - const setHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setHeader - const setHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setHeaders - const setResponseHeader: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseHeader - const setResponseHeaders: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseHeaders - const setResponseStatus: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').setResponseStatus - const splitCookiesString: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').splitCookiesString - const toEventHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toEventHandler - const toNodeListener: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toNodeListener - const toPlainHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toPlainHandler - const toWebHandler: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toWebHandler - const toWebRequest: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').toWebRequest - const unsealSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').unsealSession - const updateSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').updateSession - const useAppConfig: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/config').useAppConfig - const useBase: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').useBase - const useEvent: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/context').useEvent - const useNitroApp: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/app').useNitroApp - const useRuntimeConfig: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/config').useRuntimeConfig - const useServerAmplitude: typeof import('../../server/utils/amplitude').useServerAmplitude - const useSession: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').useSession - const useStorage: typeof import('../../node_modules/.pnpm/nitropack@2.13.1_idb-keyval@6.2.2/node_modules/nitropack/dist/runtime/internal/storage').useStorage - const users: typeof import('../../server/utils/users').users - const writeEarlyHints: typeof import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3').writeEarlyHints -} -// for type re-export -declare global { - // @ts-ignore - export type { EventHandler, EventHandlerRequest, EventHandlerResponse, EventHandlerObject, H3EventContext } from '../../node_modules/.pnpm/h3@1.15.5/node_modules/h3' - import('../../node_modules/.pnpm/h3@1.15.5/node_modules/h3') -} -export { H3Event, H3Error, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearResponseHeaders, clearSession, createApp, createAppEventHandler, createError, createEvent, createEventStream, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, defineRequestMiddleware, defineResponseMiddleware, defineWebSocket, defineWebSocketHandler, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, fromPlainHandler, fromWebHandler, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestFingerprint, getRequestHeader, getRequestHeaders, getRequestHost, getRequestIP, getRequestPath, getRequestProtocol, getRequestURL, getRequestWebStream, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, getValidatedQuery, getValidatedRouterParams, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, isWebResponse, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readFormData, readMultipartFormData, readRawBody, readValidatedBody, removeResponseHeader, sanitizeStatusCode, sanitizeStatusMessage, sealSession, send, sendError, sendIterable, sendNoContent, sendProxy, sendRedirect, sendStream, sendWebResponse, serveStatic, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, splitCookiesString, toEventHandler, toNodeListener, toPlainHandler, toWebHandler, toWebRequest, unsealSession, updateSession, useBase, useSession, writeEarlyHints } from 'h3'; -export { useNitroApp } from 'nitropack/runtime/internal/app'; -export { useRuntimeConfig, useAppConfig } from 'nitropack/runtime/internal/config'; -export { defineNitroPlugin, nitroPlugin } from 'nitropack/runtime/internal/plugin'; -export { defineCachedFunction, defineCachedEventHandler, cachedFunction, cachedEventHandler } from 'nitropack/runtime/internal/cache'; -export { useStorage } from 'nitropack/runtime/internal/storage'; -export { defineRenderHandler } from 'nitropack/runtime/internal/renderer'; -export { defineRouteMeta } from 'nitropack/runtime/internal/meta'; -export { getRouteRules } from 'nitropack/runtime/internal/route-rules'; -export { useEvent } from 'nitropack/runtime/internal/context'; -export { defineTask, runTask } from 'nitropack/runtime/internal/task'; -export { defineNitroErrorHandler } from 'nitropack/runtime/internal/error/utils'; -export { buildAssetsURL as __buildAssetsURL, publicAssetsURL as __publicAssetsURL } from '/Users/kaia/code/amp/context-hub/basics/nuxt-4/node_modules/.pnpm/@nuxt+nitro-server@4.3.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@4_e7e8ae81c59162932a5ca46c47340ed2/node_modules/@nuxt/nitro-server/dist/runtime/utils/paths'; -export { defineAppConfig } from '/Users/kaia/code/amp/context-hub/basics/nuxt-4/node_modules/.pnpm/@nuxt+nitro-server@4.3.0_db0@0.3.4_idb-keyval@6.2.2_ioredis@5.9.2_magicast@0.5.1_nuxt@4_e7e8ae81c59162932a5ca46c47340ed2/node_modules/@nuxt/nitro-server/dist/runtime/utils/config'; -export { useServerAmplitude } from '/Users/kaia/code/amp/context-hub/basics/nuxt-4/server/utils/amplitude'; -export { users, getOrCreateUser, incrementBurritoConsiderations } from '/Users/kaia/code/amp/context-hub/basics/nuxt-4/server/utils/users'; -``` - ---- - -## .nuxt/types/nitro-layouts.d.ts - -```ts -export type LayoutKey = string -declare module 'nitropack' { - interface NitroRouteConfig { - appLayout?: LayoutKey | false - } - interface NitroRouteRules { - appLayout?: LayoutKey | false - } -} -declare module 'nitropack/types' { - interface NitroRouteConfig { - appLayout?: LayoutKey | false - } - interface NitroRouteRules { - appLayout?: LayoutKey | false - } -} -``` - ---- - -## .nuxt/types/nitro-middleware.d.ts - -```ts -export type MiddlewareKey = "auth" -declare module 'nitropack/types' { - interface NitroRouteConfig { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } -} -declare module 'nitropack' { - interface NitroRouteConfig { - appMiddleware?: MiddlewareKey | MiddlewareKey[] | Record - } -} -``` - ---- - -## .nuxt/types/nitro-nuxt.d.ts - -```ts - -/// -/// -/// -/// -/// - -import type { RuntimeConfig } from 'nuxt/schema' -import type { H3Event } from 'h3' -import type { LogObject } from 'consola' -import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from 'nuxt/app' - -declare module 'nitropack' { - interface NitroRuntimeConfigApp { - buildAssetsDir: string - cdnURL: string - } - interface NitroRuntimeConfig extends RuntimeConfig {} - interface NitroRouteConfig { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - } - interface NitroRouteRules { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - appMiddleware?: Record - appLayout?: string | false - } - interface NitroRuntimeHooks { - 'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise - 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise - 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise - } -} -declare module 'nitropack/types' { - interface NitroRuntimeConfigApp { - buildAssetsDir: string - cdnURL: string - } - interface NitroRuntimeConfig extends RuntimeConfig {} - interface NitroRouteConfig { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - } - interface NitroRouteRules { - ssr?: boolean - noScripts?: boolean - /** @deprecated Use `noScripts` instead */ - experimentalNoScripts?: boolean - appMiddleware?: Record - appLayout?: string | false - } - interface NitroRuntimeHooks { - 'dev:ssr-logs': (ctx: { logs: LogObject[], path: string }) => void | Promise - 'render:html': (htmlContext: NuxtRenderHTMLContext, context: { event: H3Event }) => void | Promise - 'render:island': (islandResponse: NuxtIslandResponse, context: { event: H3Event, islandContext: NuxtIslandContext }) => void | Promise - } -} - -``` - ---- - -## .nuxt/types/nitro-routes.d.ts - -```ts -// Generated by nitro -import type { Serialize, Simplify } from "nitropack/types"; -declare module "nitropack/types" { - type Awaited = T extends PromiseLike ? Awaited : T - interface InternalApi { - '/api/auth/login': { - 'post': Simplify>>> - } - '/api/burrito/consider': { - 'post': Simplify>>> - } - '/__nuxt_error': { - 'default': Simplify>>> - } - '/__nuxt_island/**': { - 'default': Simplify>>> - } - } -} -export {} -``` - ---- - -## .nuxt/types/nitro.d.ts - -```ts -/// -/// -/// -``` - ---- - -## .nuxt/types/plugins.d.ts - -```ts -// Generated by Nuxt' -import type { Plugin } from '#app' - -type Decorate> = { [K in keyof T as K extends string ? `$${K}` : never]: T[K] } - -type InjectionType = A extends {default: Plugin} ? Decorate : unknown - -type NuxtAppInjections = - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType & - InjectionType - -declare module '#app' { - interface NuxtApp extends NuxtAppInjections { } - - interface NuxtAppLiterals { - pluginName: 'nuxt:revive-payload:client' | 'nuxt:head' | 'nuxt:router' | 'nuxt:payload' | 'nuxt:revive-payload:server' | 'nuxt:chunk-reload' | 'nuxt:global-components' | 'nuxt:prefetch' - } -} - -declare module 'vue' { - interface ComponentCustomProperties extends NuxtAppInjections { } -} - -export { } - -``` - ---- - -## .nuxt/types/runtime-config.d.ts - -```ts -import { RuntimeConfig as UserRuntimeConfig, PublicRuntimeConfig as UserPublicRuntimeConfig } from 'nuxt/schema' - interface SharedRuntimeConfig { - app: { - buildId: string, - - baseURL: string, - - buildAssetsDir: string, - - cdnURL: string, - }, - - nitro: { - envPrefix: string, - }, - } - interface SharedPublicRuntimeConfig { - amplitudeApiKey: string, - } -declare module '@nuxt/schema' { - interface RuntimeConfig extends UserRuntimeConfig {} - interface PublicRuntimeConfig extends UserPublicRuntimeConfig {} -} -declare module 'nuxt/schema' { - interface RuntimeConfig extends SharedRuntimeConfig {} - interface PublicRuntimeConfig extends SharedPublicRuntimeConfig {} -} -declare module 'vue' { - interface ComponentCustomProperties { - $config: UserRuntimeConfig - } - } -``` - ---- - -## .nuxt/types/shared-imports.d.ts - -```ts -// Generated by auto imports -export {} -declare global { - const createError: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/error').createError - const defineAppConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').defineAppConfig - const getRouteRules: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/manifest').getRouteRules - const setResponseStatus: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/composables/ssr').setResponseStatus - const useAppConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/config').useAppConfig - const useRuntimeConfig: typeof import('../../node_modules/.pnpm/nuxt@4.3.0_@parcel+watcher@2.5.6_@types+node@25.2.0_@vue+compiler-sfc@3.5.27_cac@6.7.14_10b177dc1192212ed323bb4d3b5c9b60/node_modules/nuxt/dist/app/nuxt').useRuntimeConfig -} -``` - ---- - -## .output/public/_nuxt/BaEVHuX6.js - -```js -import{_ as o,o as s,c as a,a as t,t as r}from"./BpsV6fwO.js";import{u as i}from"./Cvlh8XCj.js";const u={class:"antialiased bg-white dark:bg-[#020420] dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-[#020420] tracking-wide"},l={class:"max-w-520px text-center"},c=["textContent"],d=["textContent"],p=["textContent"],f={__name:"error-500",props:{appName:{type:String,default:"Nuxt"},status:{type:Number,default:500},statusText:{type:String,default:"Internal server error"},description:{type:String,default:"This page is temporarily unavailable."},refresh:{type:String,default:"Refresh this page"}},setup(e){const n=e;return i({title:`${n.status} - ${n.statusText} | ${n.appName}`,script:[{innerHTML:`!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`}],style:[{innerHTML:'*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}h1,h2,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }'}]}),(h,m)=>(s(),a("div",u,[t("div",l,[t("h1",{class:"font-semibold leading-none mb-4 sm:text-[110px] tabular-nums text-[80px]",textContent:r(e.status)},null,8,c),t("h2",{class:"font-semibold mb-2 sm:text-3xl text-2xl",textContent:r(e.statusText)},null,8,d),t("p",{class:"mb-4 px-2 text-[#64748B] text-md",textContent:r(e.description)},null,8,p)])]))}},x=o(f,[["__scopeId","data-v-d114bb79"]]);export{x as default}; - -``` - ---- - -## .output/public/_nuxt/BpsV6fwO.js - -```js -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./CaxW3SQp.js","./Cvlh8XCj.js","./error-404.DyKsVtYE.css","./BaEVHuX6.js","./error-500.D-HKo5t7.css"])))=>i.map(i=>d[i]); -function ey(e,t){for(var r=0;rn[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const s of o.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&n(s)}).observe(document,{childList:!0,subtree:!0});function r(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function n(i){if(i.ep)return;i.ep=!0;const o=r(i);fetch(i.href,o)}})();function Yu(e){const t=Object.create(null);for(const r of e.split(","))t[r]=1;return r=>r in t}const xe={},Cn=[],Yt=()=>{},Sh=()=>!1,Yi=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),Ju=e=>e.startsWith("onUpdate:"),Ye=Object.assign,Qu=(e,t)=>{const r=e.indexOf(t);r>-1&&e.splice(r,1)},ty=Object.prototype.hasOwnProperty,Ce=(e,t)=>ty.call(e,t),ve=Array.isArray,kn=e=>Ji(e)==="[object Map]",wh=e=>Ji(e)==="[object Set]",ry=e=>Ji(e)==="[object RegExp]",he=e=>typeof e=="function",He=e=>typeof e=="string",Ur=e=>typeof e=="symbol",De=e=>e!==null&&typeof e=="object",Th=e=>(De(e)||he(e))&&he(e.then)&&he(e.catch),Ah=Object.prototype.toString,Ji=e=>Ah.call(e),ny=e=>Ji(e).slice(8,-1),Ih=e=>Ji(e)==="[object Object]",_s=e=>He(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Zr=Yu(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Ss=e=>{const t=Object.create(null);return(r=>t[r]||(t[r]=e(r)))},iy=/-\w/g,Lt=Ss(e=>e.replace(iy,t=>t.slice(1).toUpperCase())),oy=/\B([A-Z])/g,cn=Ss(e=>e.replace(oy,"-$1").toLowerCase()),ws=Ss(e=>e.charAt(0).toUpperCase()+e.slice(1)),Ks=Ss(e=>e?`on${ws(e)}`:""),Lr=(e,t)=>!Object.is(e,t),Rn=(e,...t)=>{for(let r=0;r{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:r})},Zu=e=>{const t=parseFloat(e);return isNaN(t)?e:t},kh=e=>{const t=He(e)?Number(e):NaN;return isNaN(t)?e:t};let Jl;const Ts=()=>Jl||(Jl=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function As(e){if(ve(e)){const t={};for(let r=0;r{if(r){const n=r.split(ay);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Is(e){let t="";if(He(e))t=e;else if(ve(e))for(let r=0;r!!(e&&e.__v_isRef===!0),Oh=e=>He(e)?e:e==null?"":ve(e)||De(e)&&(e.toString===Ah||!he(e.toString))?Ph(e)?Oh(e.value):JSON.stringify(e,xh,2):String(e),xh=(e,t)=>Ph(t)?xh(e,t.value):kn(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((r,[n,i],o)=>(r[zs(n,o)+" =>"]=i,r),{})}:wh(t)?{[`Set(${t.size})`]:[...t.values()].map(r=>zs(r))}:Ur(t)?zs(t):De(t)&&!ve(t)&&!Ih(t)?String(t):t,zs=(e,t="")=>{var r;return Ur(e)?`Symbol(${(r=e.description)!=null?r:t})`:e};let mt;class Nh{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=mt,!t&&mt&&(this.index=(mt.scopes||(mt.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,r;if(this.scopes)for(t=0,r=this.scopes.length;t0&&--this._on===0&&(mt=this.prevScope,this.prevScope=void 0)}stop(t){if(this._active){this._active=!1;let r,n;for(r=0,n=this.effects.length;r0)return;if(Ti){let t=Ti;for(Ti=void 0;t;){const r=t.next;t.next=void 0,t.flags&=-9,t=r}}let e;for(;wi;){let t=wi;for(wi=void 0;t;){const r=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=r}}if(e)throw e}function Fh(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function Hh(e){let t,r=e.depsTail,n=r;for(;n;){const i=n.prevDep;n.version===-1?(n===r&&(r=i),rl(n),vy(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=i}e.deps=t,e.depsTail=r}function ja(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Bh(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Bh(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===Hi)||(e.globalVersion=Hi,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!ja(e))))return;e.flags|=2;const t=e.dep,r=Le,n=jt;Le=e,jt=!0;try{Fh(e);const i=e.fn(e._value);(t.version===0||Lr(i,e._value))&&(e.flags|=128,e._value=i,t.version++)}catch(i){throw t.version++,i}finally{Le=r,jt=n,Hh(e),e.flags&=-3}}function rl(e,t=!1){const{dep:r,prevSub:n,nextSub:i}=e;if(n&&(n.nextSub=i,e.prevSub=void 0),i&&(i.prevSub=n,e.nextSub=void 0),r.subs===e&&(r.subs=n,!n&&r.computed)){r.computed.flags&=-5;for(let o=r.computed.deps;o;o=o.nextDep)rl(o,!0)}!t&&!--r.sc&&r.map&&r.map.delete(r.key)}function vy(e){const{prevDep:t,nextDep:r}=e;t&&(t.nextDep=r,e.prevDep=void 0),r&&(r.prevDep=t,e.nextDep=void 0)}let jt=!0;const jh=[];function vr(){jh.push(jt),jt=!1}function pr(){const e=jh.pop();jt=e===void 0?!0:e}function Ql(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const r=Le;Le=void 0;try{t()}finally{Le=r}}}let Hi=0;class py{constructor(t,r){this.sub=t,this.dep=r,this.version=r.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class nl{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!Le||!jt||Le===this.computed)return;let r=this.activeLink;if(r===void 0||r.sub!==Le)r=this.activeLink=new py(Le,this),Le.deps?(r.prevDep=Le.depsTail,Le.depsTail.nextDep=r,Le.depsTail=r):Le.deps=Le.depsTail=r,qh(r);else if(r.version===-1&&(r.version=this.version,r.nextDep)){const n=r.nextDep;n.prevDep=r.prevDep,r.prevDep&&(r.prevDep.nextDep=n),r.prevDep=Le.depsTail,r.nextDep=void 0,Le.depsTail.nextDep=r,Le.depsTail=r,Le.deps===r&&(Le.deps=n)}return r}trigger(t){this.version++,Hi++,this.notify(t)}notify(t){el();try{for(let r=this.subs;r;r=r.prevSub)r.sub.notify()&&r.sub.dep.notify()}finally{tl()}}}function qh(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)qh(n)}const r=e.dep.subs;r!==e&&(e.prevSub=r,r&&(r.nextSub=e)),e.dep.subs=e}}const Qo=new WeakMap,en=Symbol(""),qa=Symbol(""),Bi=Symbol("");function st(e,t,r){if(jt&&Le){let n=Qo.get(e);n||Qo.set(e,n=new Map);let i=n.get(r);i||(n.set(r,i=new nl),i.map=n,i.key=r),i.track()}}function cr(e,t,r,n,i,o){const s=Qo.get(e);if(!s){Hi++;return}const a=u=>{u&&u.trigger()};if(el(),t==="clear")s.forEach(a);else{const u=ve(e),c=u&&_s(r);if(u&&r==="length"){const l=Number(n);s.forEach((f,d)=>{(d==="length"||d===Bi||!Ur(d)&&d>=l)&&a(f)})}else switch((r!==void 0||s.has(void 0))&&a(s.get(r)),c&&a(s.get(Bi)),t){case"add":u?c&&a(s.get("length")):(a(s.get(en)),kn(e)&&a(s.get(qa)));break;case"delete":u||(a(s.get(en)),kn(e)&&a(s.get(qa)));break;case"set":kn(e)&&a(s.get(en));break}}tl()}function gy(e,t){const r=Qo.get(e);return r&&r.get(t)}function vn(e){const t=Ae(e);return t===e?t:(st(t,"iterate",Bi),Ot(e)?t:t.map(mr))}function il(e){return st(e=Ae(e),"iterate",Bi),e}function wr(e,t){return gr(e)?ji(tn(e)?mr(t):t):mr(t)}const my={__proto__:null,[Symbol.iterator](){return Ys(this,Symbol.iterator,e=>wr(this,e))},concat(...e){return vn(this).concat(...e.map(t=>ve(t)?vn(t):t))},entries(){return Ys(this,"entries",e=>(e[1]=wr(this,e[1]),e))},every(e,t){return rr(this,"every",e,t,void 0,arguments)},filter(e,t){return rr(this,"filter",e,t,r=>r.map(n=>wr(this,n)),arguments)},find(e,t){return rr(this,"find",e,t,r=>wr(this,r),arguments)},findIndex(e,t){return rr(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return rr(this,"findLast",e,t,r=>wr(this,r),arguments)},findLastIndex(e,t){return rr(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return rr(this,"forEach",e,t,void 0,arguments)},includes(...e){return Js(this,"includes",e)},indexOf(...e){return Js(this,"indexOf",e)},join(e){return vn(this).join(e)},lastIndexOf(...e){return Js(this,"lastIndexOf",e)},map(e,t){return rr(this,"map",e,t,void 0,arguments)},pop(){return ii(this,"pop")},push(...e){return ii(this,"push",e)},reduce(e,...t){return Zl(this,"reduce",e,t)},reduceRight(e,...t){return Zl(this,"reduceRight",e,t)},shift(){return ii(this,"shift")},some(e,t){return rr(this,"some",e,t,void 0,arguments)},splice(...e){return ii(this,"splice",e)},toReversed(){return vn(this).toReversed()},toSorted(e){return vn(this).toSorted(e)},toSpliced(...e){return vn(this).toSpliced(...e)},unshift(...e){return ii(this,"unshift",e)},values(){return Ys(this,"values",e=>wr(this,e))}};function Ys(e,t,r){const n=il(e),i=n[t]();return n!==e&&!Ot(e)&&(i._next=i.next,i.next=()=>{const o=i._next();return o.done||(o.value=r(o.value)),o}),i}const yy=Array.prototype;function rr(e,t,r,n,i,o){const s=il(e),a=s!==e&&!Ot(e),u=s[t];if(u!==yy[t]){const f=u.apply(e,o);return a?mr(f):f}let c=r;s!==e&&(a?c=function(f,d){return r.call(this,wr(e,f),d,e)}:r.length>2&&(c=function(f,d){return r.call(this,f,d,e)}));const l=u.call(s,c,n);return a&&i?i(l):l}function Zl(e,t,r,n){const i=il(e);let o=r;return i!==e&&(Ot(e)?r.length>3&&(o=function(s,a,u){return r.call(this,s,a,u,e)}):o=function(s,a,u){return r.call(this,s,wr(e,a),u,e)}),i[t](o,...n)}function Js(e,t,r){const n=Ae(e);st(n,"iterate",Bi);const i=n[t](...r);return(i===-1||i===!1)&&Cs(r[0])?(r[0]=Ae(r[0]),n[t](...r)):i}function ii(e,t,r=[]){vr(),el();const n=Ae(e)[t].apply(e,r);return tl(),pr(),n}const by=Yu("__proto__,__v_isRef,__isVue"),Vh=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Ur));function Ey(e){Ur(e)||(e=String(e));const t=Ae(this);return st(t,"has",e),t.hasOwnProperty(e)}class $h{constructor(t=!1,r=!1){this._isReadonly=t,this._isShallow=r}get(t,r,n){if(r==="__v_skip")return t.__v_skip;const i=this._isReadonly,o=this._isShallow;if(r==="__v_isReactive")return!i;if(r==="__v_isReadonly")return i;if(r==="__v_isShallow")return o;if(r==="__v_raw")return n===(i?o?Py:zh:o?Kh:Gh).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const s=ve(t);if(!i){let u;if(s&&(u=my[r]))return u;if(r==="hasOwnProperty")return Ey}const a=Reflect.get(t,r,qe(t)?t:n);if((Ur(r)?Vh.has(r):by(r))||(i||st(t,"get",r),o))return a;if(qe(a)){const u=s&&_s(r)?a:a.value;return i&&De(u)?$a(u):u}return De(a)?i?$a(a):Fr(a):a}}class Wh extends $h{constructor(t=!1){super(!1,t)}set(t,r,n,i){let o=t[r];const s=ve(t)&&_s(r);if(!this._isShallow){const c=gr(o);if(!Ot(n)&&!gr(n)&&(o=Ae(o),n=Ae(n)),!s&&qe(o)&&!qe(n))return c||(o.value=n),!0}const a=s?Number(r)e,so=e=>Reflect.getPrototypeOf(e);function Ay(e,t,r){return function(...n){const i=this.__v_raw,o=Ae(i),s=kn(o),a=e==="entries"||e===Symbol.iterator&&s,u=e==="keys"&&s,c=i[e](...n),l=r?Va:t?ji:mr;return!t&&st(o,"iterate",u?qa:en),Ye(Object.create(c),{next(){const{value:f,done:d}=c.next();return d?{value:f,done:d}:{value:a?[l(f[0]),l(f[1])]:l(f),done:d}}})}}function ao(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Iy(e,t){const r={get(i){const o=this.__v_raw,s=Ae(o),a=Ae(i);e||(Lr(i,a)&&st(s,"get",i),st(s,"get",a));const{has:u}=so(s),c=t?Va:e?ji:mr;if(u.call(s,i))return c(o.get(i));if(u.call(s,a))return c(o.get(a));o!==s&&o.get(i)},get size(){const i=this.__v_raw;return!e&&st(Ae(i),"iterate",en),i.size},has(i){const o=this.__v_raw,s=Ae(o),a=Ae(i);return e||(Lr(i,a)&&st(s,"has",i),st(s,"has",a)),i===a?o.has(i):o.has(i)||o.has(a)},forEach(i,o){const s=this,a=s.__v_raw,u=Ae(a),c=t?Va:e?ji:mr;return!e&&st(u,"iterate",en),a.forEach((l,f)=>i.call(o,c(l),c(f),s))}};return Ye(r,e?{add:ao("add"),set:ao("set"),delete:ao("delete"),clear:ao("clear")}:{add(i){!t&&!Ot(i)&&!gr(i)&&(i=Ae(i));const o=Ae(this);return so(o).has.call(o,i)||(o.add(i),cr(o,"add",i,i)),this},set(i,o){!t&&!Ot(o)&&!gr(o)&&(o=Ae(o));const s=Ae(this),{has:a,get:u}=so(s);let c=a.call(s,i);c||(i=Ae(i),c=a.call(s,i));const l=u.call(s,i);return s.set(i,o),c?Lr(o,l)&&cr(s,"set",i,o):cr(s,"add",i,o),this},delete(i){const o=Ae(this),{has:s,get:a}=so(o);let u=s.call(o,i);u||(i=Ae(i),u=s.call(o,i)),a&&a.call(o,i);const c=o.delete(i);return u&&cr(o,"delete",i,void 0),c},clear(){const i=Ae(this),o=i.size!==0,s=i.clear();return o&&cr(i,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(i=>{r[i]=Ay(i,e,t)}),r}function ol(e,t){const r=Iy(e,t);return(n,i,o)=>i==="__v_isReactive"?!e:i==="__v_isReadonly"?e:i==="__v_raw"?n:Reflect.get(Ce(r,i)&&i in n?r:n,i,o)}const Cy={get:ol(!1,!1)},ky={get:ol(!1,!0)},Ry={get:ol(!0,!1)};const Gh=new WeakMap,Kh=new WeakMap,zh=new WeakMap,Py=new WeakMap;function Oy(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function xy(e){return e.__v_skip||!Object.isExtensible(e)?0:Oy(ny(e))}function Fr(e){return gr(e)?e:sl(e,!1,Sy,Cy,Gh)}function dr(e){return sl(e,!1,Ty,ky,Kh)}function $a(e){return sl(e,!0,wy,Ry,zh)}function sl(e,t,r,n,i){if(!De(e)||e.__v_raw&&!(t&&e.__v_isReactive))return e;const o=xy(e);if(o===0)return e;const s=i.get(e);if(s)return s;const a=new Proxy(e,o===2?n:r);return i.set(e,a),a}function tn(e){return gr(e)?tn(e.__v_raw):!!(e&&e.__v_isReactive)}function gr(e){return!!(e&&e.__v_isReadonly)}function Ot(e){return!!(e&&e.__v_isShallow)}function Cs(e){return e?!!e.__v_raw:!1}function Ae(e){const t=e&&e.__v_raw;return t?Ae(t):e}function Ny(e){return!Ce(e,"__v_skip")&&Object.isExtensible(e)&&Ch(e,"__v_skip",!0),e}const mr=e=>De(e)?Fr(e):e,ji=e=>De(e)?$a(e):e;function qe(e){return e?e.__v_isRef===!0:!1}function Jt(e){return Xh(e,!1)}function Bn(e){return Xh(e,!0)}function Xh(e,t){return qe(e)?e:new Ly(e,t)}class Ly{constructor(t,r){this.dep=new nl,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=r?t:Ae(t),this._value=r?t:mr(t),this.__v_isShallow=r}get value(){return this.dep.track(),this._value}set value(t){const r=this._rawValue,n=this.__v_isShallow||Ot(t)||gr(t);t=n?t:Ae(t),Lr(t,r)&&(this._rawValue=t,this._value=n?t:mr(t),this.dep.trigger())}}function ke(e){return qe(e)?e.value:e}function My(e){return he(e)?e():ke(e)}const Dy={get:(e,t,r)=>t==="__v_raw"?e:ke(Reflect.get(e,t,r)),set:(e,t,r,n)=>{const i=e[t];return qe(i)&&!qe(r)?(i.value=r,!0):Reflect.set(e,t,r,n)}};function Yh(e){return tn(e)?e:new Proxy(e,Dy)}class Uy{constructor(t,r,n){this._object=t,this._key=r,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0,this._raw=Ae(t);let i=!0,o=t;if(!ve(t)||!_s(String(r)))do i=!Cs(o)||Ot(o);while(i&&(o=o.__v_raw));this._shallow=i}get value(){let t=this._object[this._key];return this._shallow&&(t=ke(t)),this._value=t===void 0?this._defaultValue:t}set value(t){if(this._shallow&&qe(this._raw[this._key])){const r=this._object[this._key];if(qe(r)){r.value=t;return}}this._object[this._key]=t}get dep(){return gy(this._raw,this._key)}}class Fy{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function Jh(e,t,r){return qe(e)?e:he(e)?new Fy(e):De(e)&&arguments.length>1?Hy(e,t,r):Jt(e)}function Hy(e,t,r){return new Uy(e,t,r)}class By{constructor(t,r,n){this.fn=t,this.setter=r,this._value=void 0,this.dep=new nl(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=Hi-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!r,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&Le!==this)return Uh(this,!0),!0}get value(){const t=this.dep.track();return Bh(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function jy(e,t,r=!1){let n,i;return he(e)?n=e:(n=e.get,i=e.set),new By(n,i,r)}const uo={},Zo=new WeakMap;let Gr;function qy(e,t=!1,r=Gr){if(r){let n=Zo.get(r);n||Zo.set(r,n=[]),n.push(e)}}function Vy(e,t,r=xe){const{immediate:n,deep:i,once:o,scheduler:s,augmentJob:a,call:u}=r,c=y=>i?y:Ot(y)||i===!1||i===0?fr(y,1):fr(y);let l,f,d,h,v=!1,p=!1;if(qe(e)?(f=()=>e.value,v=Ot(e)):tn(e)?(f=()=>c(e),v=!0):ve(e)?(p=!0,v=e.some(y=>tn(y)||Ot(y)),f=()=>e.map(y=>{if(qe(y))return y.value;if(tn(y))return c(y);if(he(y))return u?u(y,2):y()})):he(e)?t?f=u?()=>u(e,2):e:f=()=>{if(d){vr();try{d()}finally{pr()}}const y=Gr;Gr=l;try{return u?u(e,3,[h]):e(h)}finally{Gr=y}}:f=Yt,t&&i){const y=f,_=i===!0?1/0:i;f=()=>fr(y(),_)}const b=Lh(),E=()=>{l.stop(),b&&b.active&&Qu(b.effects,l)};if(o&&t){const y=t;t=(..._)=>{y(..._),E()}}let m=p?new Array(e.length).fill(uo):uo;const g=y=>{if(!(!(l.flags&1)||!l.dirty&&!y))if(t){const _=l.run();if(i||v||(p?_.some((S,A)=>Lr(S,m[A])):Lr(_,m))){d&&d();const S=Gr;Gr=l;try{const A=[_,m===uo?void 0:p&&m[0]===uo?[]:m,h];m=_,u?u(t,3,A):t(...A)}finally{Gr=S}}}else l.run()};return a&&a(g),l=new Mh(f),l.scheduler=s?()=>s(g,!1):g,h=y=>qy(y,!1,l),d=l.onStop=()=>{const y=Zo.get(l);if(y){if(u)u(y,4);else for(const _ of y)_();Zo.delete(l)}},t?n?g(!0):m=l.run():s?s(g.bind(null,!0),!0):l.run(),E.pause=l.pause.bind(l),E.resume=l.resume.bind(l),E.stop=E,E}function fr(e,t=1/0,r){if(t<=0||!De(e)||e.__v_skip||(r=r||new Map,(r.get(e)||0)>=t))return e;if(r.set(e,t),t--,qe(e))fr(e.value,t,r);else if(ve(e))for(let n=0;n{fr(n,t,r)});else if(Ih(e)){for(const n in e)fr(e[n],t,r);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&fr(e[n],t,r)}return e}function Qi(e,t,r,n){try{return n?e(...n):e()}catch(i){Jn(i,t,r)}}function qt(e,t,r,n){if(he(e)){const i=Qi(e,t,r,n);return i&&Th(i)&&i.catch(o=>{Jn(o,t,r)}),i}if(ve(e)){const i=[];for(let o=0;o>>1,i=dt[n],o=Vi(i);o=Vi(r)?dt.push(e):dt.splice(Wy(t),0,e),e.flags|=1,Zh()}}function Zh(){es||(es=Qh.then(ev))}function Wa(e){ve(e)?Pn.push(...e):Tr&&e.id===-1?Tr.splice(bn+1,0,e):e.flags&1||(Pn.push(e),e.flags|=1),Zh()}function ec(e,t,r=Gt+1){for(;rVi(r)-Vi(n));if(Pn.length=0,Tr){Tr.push(...t);return}for(Tr=t,bn=0;bne.id==null?e.flags&2?-1:1/0:e.id;function ev(e){try{for(Gt=0;Gt{n._d&&as(-1);const o=rs(t);let s;try{s=e(...i)}finally{rs(o),n._d&&as(1)}return s};return n._n=!0,n._c=!0,n._d=!0,n}function zx(e,t){if(At===null)return e;const r=xs(At),n=e.dirs||(e.dirs=[]);for(let i=0;i1)return r&&he(t)?t.call(n&&n.proxy):t}}function ul(){return!!(Zn()||rn)}const Gy=Symbol.for("v-scx"),Ky=()=>It(Gy);function zy(e,t){return ll(e,null,t)}function xn(e,t,r){return ll(e,t,r)}function ll(e,t,r=xe){const{immediate:n,deep:i,flush:o,once:s}=r,a=Ye({},r),u=t&&n||!t&&o!=="post";let c;if($n){if(o==="sync"){const h=Ky();c=h.__watcherHandles||(h.__watcherHandles=[])}else if(!u){const h=()=>{};return h.stop=Yt,h.resume=Yt,h.pause=Yt,h}}const l=nt;a.call=(h,v,p)=>qt(h,l,v,p);let f=!1;o==="post"?a.scheduler=h=>{Je(h,l&&l.suspense)}:o!=="sync"&&(f=!0,a.scheduler=(h,v)=>{v?h():al(h)}),a.augmentJob=h=>{t&&(h.flags|=4),f&&(h.flags|=2,l&&(h.id=l.uid,h.i=l))};const d=Vy(e,t,a);return $n&&(c?c.push(d):u&&d()),d}function Xy(e,t,r){const n=this.proxy,i=He(e)?e.includes(".")?rv(n,e):()=>n[e]:e.bind(n,n);let o;he(t)?o=t:(o=t.handler,r=t);const s=eo(this),a=ll(i,o.bind(n),r);return s(),a}function rv(e,t){const r=t.split(".");return()=>{let n=e;for(let i=0;ie.__isTeleport,ar=Symbol("_leaveCb"),lo=Symbol("_enterCb");function Jy(){const e={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Rs(()=>{e.isMounted=!0}),Qn(()=>{e.isUnmounting=!0}),e}const Ct=[Function,Array],iv={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Ct,onEnter:Ct,onAfterEnter:Ct,onEnterCancelled:Ct,onBeforeLeave:Ct,onLeave:Ct,onAfterLeave:Ct,onLeaveCancelled:Ct,onBeforeAppear:Ct,onAppear:Ct,onAfterAppear:Ct,onAppearCancelled:Ct},ov=e=>{const t=e.subTree;return t.component?ov(t.component):t},Qy={name:"BaseTransition",props:iv,setup(e,{slots:t}){const r=Zn(),n=Jy();return()=>{const i=t.default&&uv(t.default(),!0);if(!i||!i.length)return;const o=sv(i),s=Ae(e),{mode:a}=s;if(n.isLeaving)return Qs(o);const u=tc(o);if(!u)return Qs(o);let c=Ga(u,s,n,r,f=>c=f);u.type!==Xe&&jn(u,c);let l=r.subTree&&tc(r.subTree);if(l&&l.type!==Xe&&!Ht(l,u)&&ov(r).type!==Xe){let f=Ga(l,s,n,r);if(jn(l,f),a==="out-in"&&u.type!==Xe)return n.isLeaving=!0,f.afterLeave=()=>{n.isLeaving=!1,r.job.flags&8||r.update(),delete f.afterLeave,l=void 0},Qs(o);a==="in-out"&&u.type!==Xe?f.delayLeave=(d,h,v)=>{const p=av(n,l);p[String(l.key)]=l,d[ar]=()=>{h(),d[ar]=void 0,delete c.delayedLeave,l=void 0},c.delayedLeave=()=>{v(),delete c.delayedLeave,l=void 0}}:l=void 0}else l&&(l=void 0);return o}}};function sv(e){let t=e[0];if(e.length>1){for(const r of e)if(r.type!==Xe){t=r;break}}return t}const Zy=Qy;function av(e,t){const{leavingVNodes:r}=e;let n=r.get(t.type);return n||(n=Object.create(null),r.set(t.type,n)),n}function Ga(e,t,r,n,i){const{appear:o,mode:s,persisted:a=!1,onBeforeEnter:u,onEnter:c,onAfterEnter:l,onEnterCancelled:f,onBeforeLeave:d,onLeave:h,onAfterLeave:v,onLeaveCancelled:p,onBeforeAppear:b,onAppear:E,onAfterAppear:m,onAppearCancelled:g}=t,y=String(e.key),_=av(r,e),S=(C,R)=>{C&&qt(C,n,9,R)},A=(C,R)=>{const P=R[1];S(C,R),ve(C)?C.every(k=>k.length<=1)&&P():C.length<=1&&P()},I={mode:s,persisted:a,beforeEnter(C){let R=u;if(!r.isMounted)if(o)R=b||u;else return;C[ar]&&C[ar](!0);const P=_[y];P&&Ht(e,P)&&P.el[ar]&&P.el[ar](),S(R,[C])},enter(C){let R=c,P=l,k=f;if(!r.isMounted)if(o)R=E||c,P=m||l,k=g||f;else return;let N=!1;const $=C[lo]=ee=>{N||(N=!0,ee?S(k,[C]):S(P,[C]),I.delayedLeave&&I.delayedLeave(),C[lo]=void 0)};R?A(R,[C,$]):$()},leave(C,R){const P=String(e.key);if(C[lo]&&C[lo](!0),r.isUnmounting)return R();S(d,[C]);let k=!1;const N=C[ar]=$=>{k||(k=!0,R(),$?S(p,[C]):S(v,[C]),C[ar]=void 0,_[P]===e&&delete _[P])};_[P]=e,h?A(h,[C,N]):N()},clone(C){const R=Ga(C,t,r,n,i);return i&&i(R),R}};return I}function Qs(e){if(Zi(e))return e=yr(e),e.children=null,e}function tc(e){if(!Zi(e))return nv(e.type)&&e.children?sv(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:r}=e;if(r){if(t&16)return r[0];if(t&32&&he(r.default))return r.default()}}function jn(e,t){e.shapeFlag&6&&e.component?(e.transition=t,jn(e.component.subTree,t)):e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function uv(e,t=!1,r){let n=[],i=0;for(let o=0;o1)for(let o=0;oNn(v,t&&(ve(t)?t[p]:t),r,n,i));return}if(Mr(n)&&!i){n.shapeFlag&512&&n.type.__asyncResolved&&n.component.subTree.component&&Nn(e,t,r,n.component.subTree);return}const o=n.shapeFlag&4?xs(n.component):n.el,s=i?null:o,{i:a,r:u}=e,c=t&&t.r,l=a.refs===xe?a.refs={}:a.refs,f=a.setupState,d=Ae(f),h=f===xe?Sh:v=>Ce(d,v);if(c!=null&&c!==u){if(rc(t),He(c))l[c]=null,h(c)&&(f[c]=null);else if(qe(c)){c.value=null;const v=t;v.k&&(l[v.k]=null)}}if(he(u))Qi(u,a,12,[s,l]);else{const v=He(u),p=qe(u);if(v||p){const b=()=>{if(e.f){const E=v?h(u)?f[u]:l[u]:u.value;if(i)ve(E)&&Qu(E,o);else if(ve(E))E.includes(o)||E.push(o);else if(v)l[u]=[o],h(u)&&(f[u]=l[u]);else{const m=[o];u.value=m,e.k&&(l[e.k]=m)}}else v?(l[u]=s,h(u)&&(f[u]=s)):p&&(u.value=s,e.k&&(l[e.k]=s))};if(s){const E=()=>{b(),ns.delete(e)};E.id=-1,ns.set(e,E),Je(E,r)}else rc(e),b()}}}function rc(e){const t=ns.get(e);t&&(t.flags|=8,ns.delete(e))}let nc=!1;const pn=()=>{nc||(console.error("Hydration completed but contains mismatches."),nc=!0)},e0=e=>e.namespaceURI.includes("svg")&&e.tagName!=="foreignObject",t0=e=>e.namespaceURI.includes("MathML"),co=e=>{if(e.nodeType===1){if(e0(e))return"svg";if(t0(e))return"mathml"}},wn=e=>e.nodeType===8;function r0(e){const{mt:t,p:r,o:{patchProp:n,createText:i,nextSibling:o,parentNode:s,remove:a,insert:u,createComment:c}}=e,l=(g,y)=>{if(!y.hasChildNodes()){r(null,g,y),ts(),y._vnode=g;return}f(y.firstChild,g,null,null,null),ts(),y._vnode=g},f=(g,y,_,S,A,I=!1)=>{I=I||!!y.dynamicChildren;const C=wn(g)&&g.data==="[",R=()=>p(g,y,_,S,A,C),{type:P,ref:k,shapeFlag:N,patchFlag:$}=y;let ee=g.nodeType;y.el=g,$===-2&&(I=!1,y.dynamicChildren=null);let B=null;switch(P){case nn:ee!==3?y.children===""?(u(y.el=i(""),s(g),g),B=g):B=R():(g.data!==y.children&&(pn(),g.data=y.children),B=o(g));break;case Xe:m(g)?(B=o(g),E(y.el=g.content.firstChild,g,_)):ee!==8||C?B=R():B=o(g);break;case No:if(C&&(g=o(g),ee=g.nodeType),ee===1||ee===3){B=g;const z=!y.children.length;for(let K=0;K{I=I||!!y.dynamicChildren;const{type:C,props:R,patchFlag:P,shapeFlag:k,dirs:N,transition:$}=y,ee=C==="input"||C==="option";if(ee||P!==-1){N&&zt(y,null,_,"created");let B=!1;if(m(g)){B=Ov(null,$)&&_&&_.vnode.props&&_.vnode.props.appear;const K=g.content.firstChild;if(B){const Q=K.getAttribute("class");Q&&(K.$cls=Q),$.beforeEnter(K)}E(K,g,_),y.el=g=K}if(k&16&&!(R&&(R.innerHTML||R.textContent))){let K=h(g.firstChild,y,g,_,S,A,I);for(;K;){fo(g,1)||pn();const Q=K;K=K.nextSibling,a(Q)}}else if(k&8){let K=y.children;K[0]===` -`&&(g.tagName==="PRE"||g.tagName==="TEXTAREA")&&(K=K.slice(1));const{textContent:Q}=g;Q!==K&&Q!==K.replace(/\r\n|\r/g,` -`)&&(fo(g,0)||pn(),g.textContent=y.children)}if(R){if(ee||!I||P&48){const K=g.tagName.includes("-");for(const Q in R)(ee&&(Q.endsWith("value")||Q==="indeterminate")||Yi(Q)&&!Zr(Q)||Q[0]==="."||K&&!Zr(Q))&&n(g,Q,null,R[Q],void 0,_)}else if(R.onClick)n(g,"onClick",null,R.onClick,void 0,_);else if(P&4&&tn(R.style))for(const K in R.style)R.style[K]}let z;(z=R&&R.onVnodeBeforeMount)&&yt(z,_,y),N&&zt(y,null,_,"beforeMount"),((z=R&&R.onVnodeMounted)||N||B)&&Uv(()=>{z&&yt(z,_,y),B&&$.enter(g),N&&zt(y,null,_,"mounted")},S)}return g.nextSibling},h=(g,y,_,S,A,I,C)=>{C=C||!!y.dynamicChildren;const R=y.children,P=R.length;for(let k=0;k{const{slotScopeIds:C}=y;C&&(A=A?A.concat(C):C);const R=s(g),P=h(o(g),y,R,_,S,A,I);return P&&wn(P)&&P.data==="]"?o(y.anchor=P):(pn(),u(y.anchor=c("]"),R,P),P)},p=(g,y,_,S,A,I)=>{if(fo(g.parentElement,1)||pn(),y.el=null,I){const P=b(g);for(;;){const k=o(g);if(k&&k!==P)a(k);else break}}const C=o(g),R=s(g);return a(g),r(null,y,R,C,_,S,co(R),A),_&&(_.vnode.el=y.el,Os(_,y.el)),C},b=(g,y="[",_="]")=>{let S=0;for(;g;)if(g=o(g),g&&wn(g)&&(g.data===y&&S++,g.data===_)){if(S===0)return o(g);S--}return g},E=(g,y,_)=>{const S=y.parentNode;S&&S.replaceChild(g,y);let A=_;for(;A;)A.vnode.el===y&&(A.vnode.el=A.subTree.el=g),A=A.parent},m=g=>g.nodeType===1&&g.tagName==="TEMPLATE";return[l,f]}const ic="data-allow-mismatch",n0={0:"text",1:"children",2:"class",3:"style",4:"attribute"};function fo(e,t){if(t===0||t===1)for(;e&&!e.hasAttribute(ic);)e=e.parentElement;const r=e&&e.getAttribute(ic);if(r==null)return!1;if(r==="")return!0;{const n=r.split(",");return t===0&&n.includes("children")?!0:n.includes(n0[t])}}Ts().requestIdleCallback;Ts().cancelIdleCallback;function i0(e,t){if(wn(e)&&e.data==="["){let r=1,n=e.nextSibling;for(;n;){if(n.nodeType===1){if(t(n)===!1)break}else if(wn(n))if(n.data==="]"){if(--r===0)break}else n.data==="["&&r++;n=n.nextSibling}}else t(e)}const Mr=e=>!!e.type.__asyncLoader;function oc(e){he(e)&&(e={loader:e});const{loader:t,loadingComponent:r,errorComponent:n,delay:i=200,hydrate:o,timeout:s,suspensible:a=!0,onError:u}=e;let c=null,l,f=0;const d=()=>(f++,c=null,h()),h=()=>{let v;return c||(v=c=t().catch(p=>{if(p=p instanceof Error?p:new Error(String(p)),u)return new Promise((b,E)=>{u(p,()=>b(d()),()=>E(p),f+1)});throw p}).then(p=>v!==c&&c?c:(p&&(p.__esModule||p[Symbol.toStringTag]==="Module")&&(p=p.default),l=p,p)))};return fn({name:"AsyncComponentWrapper",__asyncLoader:h,__asyncHydrate(v,p,b){let E=!1;(p.bu||(p.bu=[])).push(()=>E=!0);const m=()=>{E||b()},g=o?()=>{const y=o(m,_=>i0(v,_));y&&(p.bum||(p.bum=[])).push(y)}:m;l?g():h().then(()=>!p.isUnmounted&&g())},get __asyncResolved(){return l},setup(){const v=nt;if(cl(v),l)return()=>ho(l,v);const p=g=>{c=null,Jn(g,v,13,!n)};if(a&&v.suspense||$n)return h().then(g=>()=>ho(g,v)).catch(g=>(p(g),()=>n?Fe(n,{error:g}):null));const b=Jt(!1),E=Jt(),m=Jt(!!i);return i&&setTimeout(()=>{m.value=!1},i),s!=null&&setTimeout(()=>{if(!b.value&&!E.value){const g=new Error(`Async component timed out after ${s}ms.`);p(g),E.value=g}},s),h().then(()=>{b.value=!0,v.parent&&Zi(v.parent.vnode)&&v.parent.update()}).catch(g=>{p(g),E.value=g}),()=>{if(b.value&&l)return ho(l,v);if(E.value&&n)return Fe(n,{error:E.value});if(r&&!m.value)return ho(r,v)}}})}function ho(e,t){const{ref:r,props:n,children:i,ce:o}=t.vnode,s=Fe(e,n,i);return s.ref=r,s.ce=o,delete t.vnode.ce,s}const Zi=e=>e.type.__isKeepAlive,o0={name:"KeepAlive",__isKeepAlive:!0,props:{include:[String,RegExp,Array],exclude:[String,RegExp,Array],max:[String,Number]},setup(e,{slots:t}){const r=Zn(),n=r.ctx;if(!n.renderer)return()=>{const m=t.default&&t.default();return m&&m.length===1?m[0]:m};const i=new Map,o=new Set;let s=null;const a=r.suspense,{renderer:{p:u,m:c,um:l,o:{createElement:f}}}=n,d=f("div");n.activate=(m,g,y,_,S)=>{const A=m.component;c(m,g,y,0,a),u(A.vnode,m,g,y,A,a,_,m.slotScopeIds,S),Je(()=>{A.isDeactivated=!1,A.a&&Rn(A.a);const I=m.props&&m.props.onVnodeMounted;I&&yt(I,A.parent,m)},a)},n.deactivate=m=>{const g=m.component;os(g.m),os(g.a),c(m,d,null,1,a),Je(()=>{g.da&&Rn(g.da);const y=m.props&&m.props.onVnodeUnmounted;y&&yt(y,g.parent,m),g.isDeactivated=!0},a)};function h(m){Zs(m),l(m,r,a,!0)}function v(m){i.forEach((g,y)=>{const _=eu(Mr(g)?g.type.__asyncResolved||{}:g.type);_&&!m(_)&&p(y)})}function p(m){const g=i.get(m);g&&(!s||!Ht(g,s))?h(g):s&&Zs(s),i.delete(m),o.delete(m)}xn(()=>[e.include,e.exclude],([m,g])=>{m&&v(y=>bi(m,y)),g&&v(y=>!bi(g,y))},{flush:"post",deep:!0});let b=null;const E=()=>{b!=null&&(ss(r.subTree.type)?Je(()=>{i.set(b,vo(r.subTree))},r.subTree.suspense):i.set(b,vo(r.subTree)))};return Rs(E),dv(E),Qn(()=>{i.forEach(m=>{const{subTree:g,suspense:y}=r,_=vo(g);if(m.type===_.type&&m.key===_.key){Zs(_);const S=_.component.da;S&&Je(S,y);return}h(m)})}),()=>{if(b=null,!t.default)return s=null;const m=t.default(),g=m[0];if(m.length>1)return s=null,m;if(!Vn(g)||!(g.shapeFlag&4)&&!(g.shapeFlag&128))return s=null,g;let y=vo(g);if(y.type===Xe)return s=null,y;const _=y.type,S=eu(Mr(y)?y.type.__asyncResolved||{}:_),{include:A,exclude:I,max:C}=e;if(A&&(!S||!bi(A,S))||I&&S&&bi(I,S))return y.shapeFlag&=-257,s=y,g;const R=y.key==null?_:y.key,P=i.get(R);return y.el&&(y=yr(y),g.shapeFlag&128&&(g.ssContent=y)),b=R,P?(y.el=P.el,y.component=P.component,y.transition&&jn(y,y.transition),y.shapeFlag|=512,o.delete(R),o.add(R)):(o.add(R),C&&o.size>parseInt(C,10)&&p(o.values().next().value)),y.shapeFlag|=256,s=y,ss(g.type)?g:y}}},s0=o0;function bi(e,t){return ve(e)?e.some(r=>bi(r,t)):He(e)?e.split(",").includes(t):ry(e)?(e.lastIndex=0,e.test(t)):!1}function lv(e,t){fv(e,"a",t)}function cv(e,t){fv(e,"da",t)}function fv(e,t,r=nt){const n=e.__wdc||(e.__wdc=()=>{let i=r;for(;i;){if(i.isDeactivated)return;i=i.parent}return e()});if(ks(t,n,r),r){let i=r.parent;for(;i&&i.parent;)Zi(i.parent.vnode)&&a0(n,t,r,i),i=i.parent}}function a0(e,t,r,n){const i=ks(t,e,n,!0);hv(()=>{Qu(n[t],i)},r)}function Zs(e){e.shapeFlag&=-257,e.shapeFlag&=-513}function vo(e){return e.shapeFlag&128?e.ssContent:e}function ks(e,t,r=nt,n=!1){if(r){const i=r[e]||(r[e]=[]),o=t.__weh||(t.__weh=(...s)=>{vr();const a=eo(r),u=qt(t,r,e,s);return a(),pr(),u});return n?i.unshift(o):i.push(o),o}}const br=e=>(t,r=nt)=>{(!$n||e==="sp")&&ks(e,(...n)=>t(...n),r)},u0=br("bm"),Rs=br("m"),l0=br("bu"),dv=br("u"),Qn=br("bum"),hv=br("um"),c0=br("sp"),f0=br("rtg"),d0=br("rtc");function vv(e,t=nt){ks("ec",e,t)}const pv="components";function sc(e,t){return mv(pv,e,!0,t)||e}const gv=Symbol.for("v-ndc");function h0(e){return He(e)?mv(pv,e,!1)||e:e||gv}function mv(e,t,r=!0,n=!1){const i=At||nt;if(i){const o=i.type;{const a=eu(o,!1);if(a&&(a===t||a===Lt(t)||a===ws(Lt(t))))return o}const s=ac(i[e]||o[e],t)||ac(i.appContext[e],t);return!s&&n?o:s}}function ac(e,t){return e&&(e[t]||e[Lt(t)]||e[ws(Lt(t))])}const Ka=e=>e?qv(e)?xs(e):Ka(e.parent):null,Ii=Ye(Object.create(null),{$:e=>e,$el:e=>e.vnode.el,$data:e=>e.data,$props:e=>e.props,$attrs:e=>e.attrs,$slots:e=>e.slots,$refs:e=>e.refs,$parent:e=>Ka(e.parent),$root:e=>Ka(e.root),$host:e=>e.ce,$emit:e=>e.emit,$options:e=>bv(e),$forceUpdate:e=>e.f||(e.f=()=>{al(e.update)}),$nextTick:e=>e.n||(e.n=qi.bind(e.proxy)),$watch:e=>Xy.bind(e)}),ea=(e,t)=>e!==xe&&!e.__isScriptSetup&&Ce(e,t),v0={get({_:e},t){if(t==="__v_skip")return!0;const{ctx:r,setupState:n,data:i,props:o,accessCache:s,type:a,appContext:u}=e;if(t[0]!=="$"){const d=s[t];if(d!==void 0)switch(d){case 1:return n[t];case 2:return i[t];case 4:return r[t];case 3:return o[t]}else{if(ea(n,t))return s[t]=1,n[t];if(i!==xe&&Ce(i,t))return s[t]=2,i[t];if(Ce(o,t))return s[t]=3,o[t];if(r!==xe&&Ce(r,t))return s[t]=4,r[t];za&&(s[t]=0)}}const c=Ii[t];let l,f;if(c)return t==="$attrs"&&st(e.attrs,"get",""),c(e);if((l=a.__cssModules)&&(l=l[t]))return l;if(r!==xe&&Ce(r,t))return s[t]=4,r[t];if(f=u.config.globalProperties,Ce(f,t))return f[t]},set({_:e},t,r){const{data:n,setupState:i,ctx:o}=e;return ea(i,t)?(i[t]=r,!0):n!==xe&&Ce(n,t)?(n[t]=r,!0):Ce(e.props,t)||t[0]==="$"&&t.slice(1)in e?!1:(o[t]=r,!0)},has({_:{data:e,setupState:t,accessCache:r,ctx:n,appContext:i,props:o,type:s}},a){let u;return!!(r[a]||e!==xe&&a[0]!=="$"&&Ce(e,a)||ea(t,a)||Ce(o,a)||Ce(n,a)||Ce(Ii,a)||Ce(i.config.globalProperties,a)||(u=s.__cssModules)&&u[a])},defineProperty(e,t,r){return r.get!=null?e._.accessCache[t]=0:Ce(r,"value")&&this.set(e,t,r.value,null),Reflect.defineProperty(e,t,r)}};function uc(e){return ve(e)?e.reduce((t,r)=>(t[r]=null,t),{}):e}let za=!0;function p0(e){const t=bv(e),r=e.proxy,n=e.ctx;za=!1,t.beforeCreate&&lc(t.beforeCreate,e,"bc");const{data:i,computed:o,methods:s,watch:a,provide:u,inject:c,created:l,beforeMount:f,mounted:d,beforeUpdate:h,updated:v,activated:p,deactivated:b,beforeDestroy:E,beforeUnmount:m,destroyed:g,unmounted:y,render:_,renderTracked:S,renderTriggered:A,errorCaptured:I,serverPrefetch:C,expose:R,inheritAttrs:P,components:k,directives:N,filters:$}=t;if(c&&g0(c,n,null),s)for(const z in s){const K=s[z];he(K)&&(n[z]=K.bind(r))}if(i){const z=i.call(r,r);De(z)&&(e.data=Fr(z))}if(za=!0,o)for(const z in o){const K=o[z],Q=he(K)?K.bind(r,r):he(K.get)?K.get.bind(r,r):Yt,ae=!he(K)&&he(K.set)?K.set.bind(r):Yt,fe=We({get:Q,set:ae});Object.defineProperty(n,z,{enumerable:!0,configurable:!0,get:()=>fe.value,set:pe=>fe.value=pe})}if(a)for(const z in a)yv(a[z],n,r,z);if(u){const z=he(u)?u.call(r):u;Reflect.ownKeys(z).forEach(K=>{On(K,z[K])})}l&&lc(l,e,"c");function B(z,K){ve(K)?K.forEach(Q=>z(Q.bind(r))):K&&z(K.bind(r))}if(B(u0,f),B(Rs,d),B(l0,h),B(dv,v),B(lv,p),B(cv,b),B(vv,I),B(d0,S),B(f0,A),B(Qn,m),B(hv,y),B(c0,C),ve(R))if(R.length){const z=e.exposed||(e.exposed={});R.forEach(K=>{Object.defineProperty(z,K,{get:()=>r[K],set:Q=>r[K]=Q,enumerable:!0})})}else e.exposed||(e.exposed={});_&&e.render===Yt&&(e.render=_),P!=null&&(e.inheritAttrs=P),k&&(e.components=k),N&&(e.directives=N),C&&cl(e)}function g0(e,t,r=Yt){ve(e)&&(e=Xa(e));for(const n in e){const i=e[n];let o;De(i)?"default"in i?o=It(i.from||n,i.default,!0):o=It(i.from||n):o=It(i),qe(o)?Object.defineProperty(t,n,{enumerable:!0,configurable:!0,get:()=>o.value,set:s=>o.value=s}):t[n]=o}}function lc(e,t,r){qt(ve(e)?e.map(n=>n.bind(t.proxy)):e.bind(t.proxy),t,r)}function yv(e,t,r,n){let i=n.includes(".")?rv(r,n):()=>r[n];if(He(e)){const o=t[e];he(o)&&xn(i,o)}else if(he(e))xn(i,e.bind(r));else if(De(e))if(ve(e))e.forEach(o=>yv(o,t,r,n));else{const o=he(e.handler)?e.handler.bind(r):t[e.handler];he(o)&&xn(i,o,e)}}function bv(e){const t=e.type,{mixins:r,extends:n}=t,{mixins:i,optionsCache:o,config:{optionMergeStrategies:s}}=e.appContext,a=o.get(t);let u;return a?u=a:!i.length&&!r&&!n?u=t:(u={},i.length&&i.forEach(c=>is(u,c,s,!0)),is(u,t,s)),De(t)&&o.set(t,u),u}function is(e,t,r,n=!1){const{mixins:i,extends:o}=t;o&&is(e,o,r,!0),i&&i.forEach(s=>is(e,s,r,!0));for(const s in t)if(!(n&&s==="expose")){const a=m0[s]||r&&r[s];e[s]=a?a(e[s],t[s]):t[s]}return e}const m0={data:cc,props:fc,emits:fc,methods:Ei,computed:Ei,beforeCreate:lt,created:lt,beforeMount:lt,mounted:lt,beforeUpdate:lt,updated:lt,beforeDestroy:lt,beforeUnmount:lt,destroyed:lt,unmounted:lt,activated:lt,deactivated:lt,errorCaptured:lt,serverPrefetch:lt,components:Ei,directives:Ei,watch:b0,provide:cc,inject:y0};function cc(e,t){return t?e?function(){return Ye(he(e)?e.call(this,this):e,he(t)?t.call(this,this):t)}:t:e}function y0(e,t){return Ei(Xa(e),Xa(t))}function Xa(e){if(ve(e)){const t={};for(let r=0;rt==="modelValue"||t==="model-value"?e.modelModifiers:e[`${t}Modifiers`]||e[`${Lt(t)}Modifiers`]||e[`${cn(t)}Modifiers`];function w0(e,t,...r){if(e.isUnmounted)return;const n=e.vnode.props||xe;let i=r;const o=t.startsWith("update:"),s=o&&S0(n,t.slice(7));s&&(s.trim&&(i=r.map(l=>He(l)?l.trim():l)),s.number&&(i=r.map(Zu)));let a,u=n[a=Ks(t)]||n[a=Ks(Lt(t))];!u&&o&&(u=n[a=Ks(cn(t))]),u&&qt(u,e,6,i);const c=n[a+"Once"];if(c){if(!e.emitted)e.emitted={};else if(e.emitted[a])return;e.emitted[a]=!0,qt(c,e,6,i)}}const T0=new WeakMap;function _v(e,t,r=!1){const n=r?T0:t.emitsCache,i=n.get(e);if(i!==void 0)return i;const o=e.emits;let s={},a=!1;if(!he(e)){const u=c=>{const l=_v(c,t,!0);l&&(a=!0,Ye(s,l))};!r&&t.mixins.length&&t.mixins.forEach(u),e.extends&&u(e.extends),e.mixins&&e.mixins.forEach(u)}return!o&&!a?(De(e)&&n.set(e,null),null):(ve(o)?o.forEach(u=>s[u]=null):Ye(s,o),De(e)&&n.set(e,s),s)}function Ps(e,t){return!e||!Yi(t)?!1:(t=t.slice(2).replace(/Once$/,""),Ce(e,t[0].toLowerCase()+t.slice(1))||Ce(e,cn(t))||Ce(e,t))}function ta(e){const{type:t,vnode:r,proxy:n,withProxy:i,propsOptions:[o],slots:s,attrs:a,emit:u,render:c,renderCache:l,props:f,data:d,setupState:h,ctx:v,inheritAttrs:p}=e,b=rs(e);let E,m;try{if(r.shapeFlag&4){const y=i||n,_=y;E=Tt(c.call(_,y,l,f,h,d,v)),m=a}else{const y=t;E=Tt(y.length>1?y(f,{attrs:a,slots:s,emit:u}):y(f,null)),m=t.props?a:I0(a)}}catch(y){Ci.length=0,Jn(y,e,1),E=Fe(Xe)}let g=E;if(m&&p!==!1){const y=Object.keys(m),{shapeFlag:_}=g;y.length&&_&7&&(o&&y.some(Ju)&&(m=C0(m,o)),g=yr(g,m,!1,!0))}return r.dirs&&(g=yr(g,null,!1,!0),g.dirs=g.dirs?g.dirs.concat(r.dirs):r.dirs),r.transition&&jn(g,r.transition),E=g,rs(b),E}function A0(e,t=!0){let r;for(let n=0;n{let t;for(const r in e)(r==="class"||r==="style"||Yi(r))&&((t||(t={}))[r]=e[r]);return t},C0=(e,t)=>{const r={};for(const n in e)(!Ju(n)||!(n.slice(9)in t))&&(r[n]=e[n]);return r};function k0(e,t,r){const{props:n,children:i,component:o}=e,{props:s,children:a,patchFlag:u}=t,c=o.emitsOptions;if(t.dirs||t.transition)return!0;if(r&&u>=0){if(u&1024)return!0;if(u&16)return n?dc(n,s,c):!!s;if(u&8){const l=t.dynamicProps;for(let f=0;fObject.create(Sv),Tv=e=>Object.getPrototypeOf(e)===Sv;function R0(e,t,r,n=!1){const i={},o=wv();e.propsDefaults=Object.create(null),Av(e,t,i,o);for(const s in e.propsOptions[0])s in i||(i[s]=void 0);r?e.props=n?i:dr(i):e.type.props?e.props=i:e.props=o,e.attrs=o}function P0(e,t,r,n){const{props:i,attrs:o,vnode:{patchFlag:s}}=e,a=Ae(i),[u]=e.propsOptions;let c=!1;if((n||s>0)&&!(s&16)){if(s&8){const l=e.vnode.dynamicProps;for(let f=0;f{u=!0;const[d,h]=Iv(f,t,!0);Ye(s,d),h&&a.push(...h)};!r&&t.mixins.length&&t.mixins.forEach(l),e.extends&&l(e.extends),e.mixins&&e.mixins.forEach(l)}if(!o&&!u)return De(e)&&n.set(e,Cn),Cn;if(ve(o))for(let l=0;le==="_"||e==="_ctx"||e==="$stable",dl=e=>ve(e)?e.map(Tt):[Tt(e)],x0=(e,t,r)=>{if(t._n)return t;const n=Ai((...i)=>dl(t(...i)),r);return n._c=!1,n},Cv=(e,t,r)=>{const n=e._ctx;for(const i in e){if(fl(i))continue;const o=e[i];if(he(o))t[i]=x0(i,o,n);else if(o!=null){const s=dl(o);t[i]=()=>s}}},kv=(e,t)=>{const r=dl(t);e.slots.default=()=>r},Rv=(e,t,r)=>{for(const n in t)(r||!fl(n))&&(e[n]=t[n])},N0=(e,t,r)=>{const n=e.slots=wv();if(e.vnode.shapeFlag&32){const i=t._;i?(Rv(n,t,r),r&&Ch(n,"_",i,!0)):Cv(t,n)}else t&&kv(e,t)},L0=(e,t,r)=>{const{vnode:n,slots:i}=e;let o=!0,s=xe;if(n.shapeFlag&32){const a=t._;a?r&&a===1?o=!1:Rv(i,t,r):(o=!t.$stable,Cv(t,i)),s=t}else t&&(kv(e,t),s={default:1});if(o)for(const a in i)!fl(a)&&s[a]==null&&delete i[a]},Je=Uv;function M0(e){return Pv(e)}function D0(e){return Pv(e,r0)}function Pv(e,t){const r=Ts();r.__VUE__=!0;const{insert:n,remove:i,patchProp:o,createElement:s,createText:a,createComment:u,setText:c,setElementText:l,parentNode:f,nextSibling:d,setScopeId:h=Yt,insertStaticContent:v}=e,p=(w,T,L,H=null,U=null,q=null,te=void 0,G=null,Y=!!T.dynamicChildren)=>{if(w===T)return;w&&!Ht(w,T)&&(H=M(w),pe(w,U,q,!0),w=null),T.patchFlag===-2&&(Y=!1,T.dynamicChildren=null);const{type:V,ref:W,shapeFlag:j}=T;switch(V){case nn:b(w,T,L,H);break;case Xe:E(w,T,L,H);break;case No:w==null&&m(T,L,H,te);break;case ht:k(w,T,L,H,U,q,te,G,Y);break;default:j&1?_(w,T,L,H,U,q,te,G,Y):j&6?N(w,T,L,H,U,q,te,G,Y):(j&64||j&128)&&V.process(w,T,L,H,U,q,te,G,Y,re)}W!=null&&U?Nn(W,w&&w.ref,q,T||w,!T):W==null&&w&&w.ref!=null&&Nn(w.ref,null,q,w,!0)},b=(w,T,L,H)=>{if(w==null)n(T.el=a(T.children),L,H);else{const U=T.el=w.el;T.children!==w.children&&c(U,T.children)}},E=(w,T,L,H)=>{w==null?n(T.el=u(T.children||""),L,H):T.el=w.el},m=(w,T,L,H)=>{[w.el,w.anchor]=v(w.children,T,L,H,w.el,w.anchor)},g=({el:w,anchor:T},L,H)=>{let U;for(;w&&w!==T;)U=d(w),n(w,L,H),w=U;n(T,L,H)},y=({el:w,anchor:T})=>{let L;for(;w&&w!==T;)L=d(w),i(w),w=L;i(T)},_=(w,T,L,H,U,q,te,G,Y)=>{if(T.type==="svg"?te="svg":T.type==="math"&&(te="mathml"),w==null)S(T,L,H,U,q,te,G,Y);else{const V=w.el&&w.el._isVueCE?w.el:null;try{V&&V._beginPatch(),C(w,T,U,q,te,G,Y)}finally{V&&V._endPatch()}}},S=(w,T,L,H,U,q,te,G)=>{let Y,V;const{props:W,shapeFlag:j,transition:J,dirs:ne}=w;if(Y=w.el=s(w.type,q,W&&W.is,W),j&8?l(Y,w.children):j&16&&I(w.children,Y,null,H,U,ra(w,q),te,G),ne&&zt(w,null,H,"created"),A(Y,w,w.scopeId,te,H),W){for(const Se in W)Se!=="value"&&!Zr(Se)&&o(Y,Se,null,W[Se],q,H);"value"in W&&o(Y,"value",null,W.value,q),(V=W.onVnodeBeforeMount)&&yt(V,H,w)}ne&&zt(w,null,H,"beforeMount");const ie=Ov(U,J);ie&&J.beforeEnter(Y),n(Y,T,L),((V=W&&W.onVnodeMounted)||ie||ne)&&Je(()=>{V&&yt(V,H,w),ie&&J.enter(Y),ne&&zt(w,null,H,"mounted")},U)},A=(w,T,L,H,U)=>{if(L&&h(w,L),H)for(let q=0;q{for(let V=Y;V{const G=T.el=w.el;let{patchFlag:Y,dynamicChildren:V,dirs:W}=T;Y|=w.patchFlag&16;const j=w.props||xe,J=T.props||xe;let ne;if(L&&Br(L,!1),(ne=J.onVnodeBeforeUpdate)&&yt(ne,L,T,w),W&&zt(T,w,L,"beforeUpdate"),L&&Br(L,!0),(j.innerHTML&&J.innerHTML==null||j.textContent&&J.textContent==null)&&l(G,""),V?R(w.dynamicChildren,V,G,L,H,ra(T,U),q):te||K(w,T,G,null,L,H,ra(T,U),q,!1),Y>0){if(Y&16)P(G,j,J,L,U);else if(Y&2&&j.class!==J.class&&o(G,"class",null,J.class,U),Y&4&&o(G,"style",j.style,J.style,U),Y&8){const ie=T.dynamicProps;for(let Se=0;Se{ne&&yt(ne,L,T,w),W&&zt(T,w,L,"updated")},H)},R=(w,T,L,H,U,q,te)=>{for(let G=0;G{if(T!==L){if(T!==xe)for(const q in T)!Zr(q)&&!(q in L)&&o(w,q,T[q],null,U,H);for(const q in L){if(Zr(q))continue;const te=L[q],G=T[q];te!==G&&q!=="value"&&o(w,q,G,te,U,H)}"value"in L&&o(w,"value",T.value,L.value,U)}},k=(w,T,L,H,U,q,te,G,Y)=>{const V=T.el=w?w.el:a(""),W=T.anchor=w?w.anchor:a("");let{patchFlag:j,dynamicChildren:J,slotScopeIds:ne}=T;ne&&(G=G?G.concat(ne):ne),w==null?(n(V,L,H),n(W,L,H),I(T.children||[],L,W,U,q,te,G,Y)):j>0&&j&64&&J&&w.dynamicChildren&&w.dynamicChildren.length===J.length?(R(w.dynamicChildren,J,L,U,q,te,G),(T.key!=null||U&&T===U.subTree)&&xv(w,T,!0)):K(w,T,L,W,U,q,te,G,Y)},N=(w,T,L,H,U,q,te,G,Y)=>{T.slotScopeIds=G,w==null?T.shapeFlag&512?U.ctx.activate(T,L,H,te,Y):$(T,L,H,U,q,te,Y):ee(w,T,Y)},$=(w,T,L,H,U,q,te)=>{const G=w.component=z0(w,H,U);if(Zi(w)&&(G.ctx.renderer=re),X0(G,!1,te),G.asyncDep){if(U&&U.registerDep(G,B,te),!w.el){const Y=G.subTree=Fe(Xe);E(null,Y,T,L),w.placeholder=Y.el}}else B(G,w,T,L,U,q,te)},ee=(w,T,L)=>{const H=T.component=w.component;if(k0(w,T,L))if(H.asyncDep&&!H.asyncResolved){z(H,T,L);return}else H.next=T,H.update();else T.el=w.el,H.vnode=T},B=(w,T,L,H,U,q,te)=>{const G=()=>{if(w.isMounted){let{next:j,bu:J,u:ne,parent:ie,vnode:Se}=w;{const it=Nv(w);if(it){j&&(j.el=Se.el,z(w,j,te)),it.asyncDep.then(()=>{w.isUnmounted||G()});return}}let ue=j,$e;Br(w,!1),j?(j.el=Se.el,z(w,j,te)):j=Se,J&&Rn(J),($e=j.props&&j.props.onVnodeBeforeUpdate)&&yt($e,ie,j,Se),Br(w,!0);const Oe=ta(w),gt=w.subTree;w.subTree=Oe,p(gt,Oe,f(gt.el),M(gt),w,U,q),j.el=Oe.el,ue===null&&Os(w,Oe.el),ne&&Je(ne,U),($e=j.props&&j.props.onVnodeUpdated)&&Je(()=>yt($e,ie,j,Se),U)}else{let j;const{el:J,props:ne}=T,{bm:ie,m:Se,parent:ue,root:$e,type:Oe}=w,gt=Mr(T);if(Br(w,!1),ie&&Rn(ie),!gt&&(j=ne&&ne.onVnodeBeforeMount)&&yt(j,ue,T),Br(w,!0),J&&ge){const it=()=>{w.subTree=ta(w),ge(J,w.subTree,w,U,null)};gt&&Oe.__asyncHydrate?Oe.__asyncHydrate(J,w,it):it()}else{$e.ce&&$e.ce._def.shadowRoot!==!1&&$e.ce._injectChildStyle(Oe);const it=w.subTree=ta(w);p(null,it,L,H,w,U,q),T.el=it.el}if(Se&&Je(Se,U),!gt&&(j=ne&&ne.onVnodeMounted)){const it=T;Je(()=>yt(j,ue,it),U)}(T.shapeFlag&256||ue&&Mr(ue.vnode)&&ue.vnode.shapeFlag&256)&&w.a&&Je(w.a,U),w.isMounted=!0,T=L=H=null}};w.scope.on();const Y=w.effect=new Mh(G);w.scope.off();const V=w.update=Y.run.bind(Y),W=w.job=Y.runIfDirty.bind(Y);W.i=w,W.id=w.uid,Y.scheduler=()=>al(W),Br(w,!0),V()},z=(w,T,L)=>{T.component=w;const H=w.vnode.props;w.vnode=T,w.next=null,P0(w,T.props,H,L),L0(w,T.children,L),vr(),ec(w),pr()},K=(w,T,L,H,U,q,te,G,Y=!1)=>{const V=w&&w.children,W=w?w.shapeFlag:0,j=T.children,{patchFlag:J,shapeFlag:ne}=T;if(J>0){if(J&128){ae(V,j,L,H,U,q,te,G,Y);return}else if(J&256){Q(V,j,L,H,U,q,te,G,Y);return}}ne&8?(W&16&&me(V,U,q),j!==V&&l(L,j)):W&16?ne&16?ae(V,j,L,H,U,q,te,G,Y):me(V,U,q,!0):(W&8&&l(L,""),ne&16&&I(j,L,H,U,q,te,G,Y))},Q=(w,T,L,H,U,q,te,G,Y)=>{w=w||Cn,T=T||Cn;const V=w.length,W=T.length,j=Math.min(V,W);let J;for(J=0;JW?me(w,U,q,!0,!1,j):I(T,L,H,U,q,te,G,Y,j)},ae=(w,T,L,H,U,q,te,G,Y)=>{let V=0;const W=T.length;let j=w.length-1,J=W-1;for(;V<=j&&V<=J;){const ne=w[V],ie=T[V]=Y?Ar(T[V]):Tt(T[V]);if(Ht(ne,ie))p(ne,ie,L,null,U,q,te,G,Y);else break;V++}for(;V<=j&&V<=J;){const ne=w[j],ie=T[J]=Y?Ar(T[J]):Tt(T[J]);if(Ht(ne,ie))p(ne,ie,L,null,U,q,te,G,Y);else break;j--,J--}if(V>j){if(V<=J){const ne=J+1,ie=neJ)for(;V<=j;)pe(w[V],U,q,!0),V++;else{const ne=V,ie=V,Se=new Map;for(V=ie;V<=J;V++){const wt=T[V]=Y?Ar(T[V]):Tt(T[V]);wt.key!=null&&Se.set(wt.key,V)}let ue,$e=0;const Oe=J-ie+1;let gt=!1,it=0;const ni=new Array(Oe);for(V=0;V=Oe){pe(wt,U,q,!0);continue}let Wt;if(wt.key!=null)Wt=Se.get(wt.key);else for(ue=ie;ue<=J;ue++)if(ni[ue-ie]===0&&Ht(wt,T[ue])){Wt=ue;break}Wt===void 0?pe(wt,U,q,!0):(ni[Wt-ie]=V+1,Wt>=it?it=Wt:gt=!0,p(wt,T[Wt],L,null,U,q,te,G,Y),$e++)}const zl=gt?U0(ni):Cn;for(ue=zl.length-1,V=Oe-1;V>=0;V--){const wt=ie+V,Wt=T[wt],Xl=T[wt+1],Yl=wt+1{const{el:q,type:te,transition:G,children:Y,shapeFlag:V}=w;if(V&6){fe(w.component.subTree,T,L,H);return}if(V&128){w.suspense.move(T,L,H);return}if(V&64){te.move(w,T,L,re);return}if(te===ht){n(q,T,L);for(let j=0;jG.enter(q),U);else{const{leave:j,delayLeave:J,afterLeave:ne}=G,ie=()=>{w.ctx.isUnmounted?i(q):n(q,T,L)},Se=()=>{q._isLeaving&&q[ar](!0),j(q,()=>{ie(),ne&&ne()})};J?J(q,ie,Se):Se()}else n(q,T,L)},pe=(w,T,L,H=!1,U=!1)=>{const{type:q,props:te,ref:G,children:Y,dynamicChildren:V,shapeFlag:W,patchFlag:j,dirs:J,cacheIndex:ne}=w;if(j===-2&&(U=!1),G!=null&&(vr(),Nn(G,null,L,w,!0),pr()),ne!=null&&(T.renderCache[ne]=void 0),W&256){T.ctx.deactivate(w);return}const ie=W&1&&J,Se=!Mr(w);let ue;if(Se&&(ue=te&&te.onVnodeBeforeUnmount)&&yt(ue,T,w),W&6)de(w.component,L,H);else{if(W&128){w.suspense.unmount(L,H);return}ie&&zt(w,null,T,"beforeUnmount"),W&64?w.type.remove(w,T,L,re,H):V&&!V.hasOnce&&(q!==ht||j>0&&j&64)?me(V,T,L,!1,!0):(q===ht&&j&384||!U&&W&16)&&me(Y,T,L),H&&ye(w)}(Se&&(ue=te&&te.onVnodeUnmounted)||ie)&&Je(()=>{ue&&yt(ue,T,w),ie&&zt(w,null,T,"unmounted")},L)},ye=w=>{const{type:T,el:L,anchor:H,transition:U}=w;if(T===ht){be(L,H);return}if(T===No){y(w);return}const q=()=>{i(L),U&&!U.persisted&&U.afterLeave&&U.afterLeave()};if(w.shapeFlag&1&&U&&!U.persisted){const{leave:te,delayLeave:G}=U,Y=()=>te(L,q);G?G(w.el,q,Y):Y()}else q()},be=(w,T)=>{let L;for(;w!==T;)L=d(w),i(w),w=L;i(T)},de=(w,T,L)=>{const{bum:H,scope:U,job:q,subTree:te,um:G,m:Y,a:V}=w;os(Y),os(V),H&&Rn(H),U.stop(),q&&(q.flags|=8,pe(te,w,T,L)),G&&Je(G,T),Je(()=>{w.isUnmounted=!0},T)},me=(w,T,L,H=!1,U=!1,q=0)=>{for(let te=q;te{if(w.shapeFlag&6)return M(w.component.subTree);if(w.shapeFlag&128)return w.suspense.next();const T=d(w.anchor||w.el),L=T&&T[Yy];return L?d(L):T};let Z=!1;const X=(w,T,L)=>{let H;w==null?T._vnode&&(pe(T._vnode,null,null,!0),H=T._vnode.component):p(T._vnode||null,w,T,null,null,null,L),T._vnode=w,Z||(Z=!0,ec(H),ts(),Z=!1)},re={p,um:pe,m:fe,r:ye,mt:$,mc:I,pc:K,pbc:R,n:M,o:e};let F,ge;return t&&([F,ge]=t(re)),{render:X,hydrate:F,createApp:_0(X,F)}}function ra({type:e,props:t},r){return r==="svg"&&e==="foreignObject"||r==="mathml"&&e==="annotation-xml"&&t&&t.encoding&&t.encoding.includes("html")?void 0:r}function Br({effect:e,job:t},r){r?(e.flags|=32,t.flags|=4):(e.flags&=-33,t.flags&=-5)}function Ov(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function xv(e,t,r=!1){const n=e.children,i=t.children;if(ve(n)&&ve(i))for(let o=0;o>1,e[r[a]]0&&(t[n]=r[o-1]),r[o]=n)}}for(o=r.length,s=r[o-1];o-- >0;)r[o]=s,s=t[s];return r}function Nv(e){const t=e.subTree.component;if(t)return t.asyncDep&&!t.asyncResolved?t:Nv(t)}function os(e){if(e)for(let t=0;te.__isSuspense;let Ja=0;const F0={name:"Suspense",__isSuspense:!0,process(e,t,r,n,i,o,s,a,u,c){if(e==null)H0(t,r,n,i,o,s,a,u,c);else{if(o&&o.deps>0&&!e.suspense.isInFallback){t.suspense=e.suspense,t.suspense.vnode=t,t.el=e.el;return}B0(e,t,r,n,i,s,a,u,c)}},hydrate:j0,normalize:q0},Mv=F0;function $i(e,t){const r=e.props&&e.props[t];he(r)&&r()}function H0(e,t,r,n,i,o,s,a,u){const{p:c,o:{createElement:l}}=u,f=l("div"),d=e.suspense=Dv(e,i,n,t,f,r,o,s,a,u);c(null,d.pendingBranch=e.ssContent,f,null,n,d,o,s),d.deps>0?($i(e,"onPending"),$i(e,"onFallback"),c(null,e.ssFallback,t,r,n,null,o,s),Ln(d,e.ssFallback)):d.resolve(!1,!0)}function B0(e,t,r,n,i,o,s,a,{p:u,um:c,o:{createElement:l}}){const f=t.suspense=e.suspense;f.vnode=t,t.el=e.el;const d=t.ssContent,h=t.ssFallback,{activeBranch:v,pendingBranch:p,isInFallback:b,isHydrating:E}=f;if(p)f.pendingBranch=d,Ht(p,d)?(u(p,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0?f.resolve():b&&(E||(u(v,h,r,n,i,null,o,s,a),Ln(f,h)))):(f.pendingId=Ja++,E?(f.isHydrating=!1,f.activeBranch=p):c(p,i,f),f.deps=0,f.effects.length=0,f.hiddenContainer=l("div"),b?(u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0?f.resolve():(u(v,h,r,n,i,null,o,s,a),Ln(f,h))):v&&Ht(v,d)?(u(v,d,r,n,i,f,o,s,a),f.resolve(!0)):(u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0&&f.resolve()));else if(v&&Ht(v,d))u(v,d,r,n,i,f,o,s,a),Ln(f,d);else if($i(t,"onPending"),f.pendingBranch=d,d.shapeFlag&512?f.pendingId=d.component.suspenseId:f.pendingId=Ja++,u(null,d,f.hiddenContainer,null,i,f,o,s,a),f.deps<=0)f.resolve();else{const{timeout:m,pendingId:g}=f;m>0?setTimeout(()=>{f.pendingId===g&&f.fallback(h)},m):m===0&&f.fallback(h)}}function Dv(e,t,r,n,i,o,s,a,u,c,l=!1){const{p:f,m:d,um:h,n:v,o:{parentNode:p,remove:b}}=c;let E;const m=V0(e);m&&t&&t.pendingBranch&&(E=t.pendingId,t.deps++);const g=e.props?kh(e.props.timeout):void 0,y=o,_={vnode:e,parent:t,parentComponent:r,namespace:s,container:n,hiddenContainer:i,deps:0,pendingId:Ja++,timeout:typeof g=="number"?g:-1,activeBranch:null,pendingBranch:null,isInFallback:!l,isHydrating:l,isUnmounted:!1,effects:[],resolve(S=!1,A=!1){const{vnode:I,activeBranch:C,pendingBranch:R,pendingId:P,effects:k,parentComponent:N,container:$,isInFallback:ee}=_;let B=!1;_.isHydrating?_.isHydrating=!1:S||(B=C&&R.transition&&R.transition.mode==="out-in",B&&(C.transition.afterLeave=()=>{P===_.pendingId&&(d(R,$,o===y?v(C):o,0),Wa(k),ee&&I.ssFallback&&(I.ssFallback.el=null))}),C&&(p(C.el)===$&&(o=v(C)),h(C,N,_,!0),!B&&ee&&I.ssFallback&&Je(()=>I.ssFallback.el=null,_)),B||d(R,$,o,0)),Ln(_,R),_.pendingBranch=null,_.isInFallback=!1;let z=_.parent,K=!1;for(;z;){if(z.pendingBranch){z.effects.push(...k),K=!0;break}z=z.parent}!K&&!B&&Wa(k),_.effects=[],m&&t&&t.pendingBranch&&E===t.pendingId&&(t.deps--,t.deps===0&&!A&&t.resolve()),$i(I,"onResolve")},fallback(S){if(!_.pendingBranch)return;const{vnode:A,activeBranch:I,parentComponent:C,container:R,namespace:P}=_;$i(A,"onFallback");const k=v(I),N=()=>{_.isInFallback&&(f(null,S,R,k,C,null,P,a,u),Ln(_,S))},$=S.transition&&S.transition.mode==="out-in";$&&(I.transition.afterLeave=N),_.isInFallback=!0,h(I,C,null,!0),$||N()},move(S,A,I){_.activeBranch&&d(_.activeBranch,S,A,I),_.container=S},next(){return _.activeBranch&&v(_.activeBranch)},registerDep(S,A,I){const C=!!_.pendingBranch;C&&_.deps++;const R=S.vnode.el;S.asyncDep.catch(P=>{Jn(P,S,0)}).then(P=>{if(S.isUnmounted||_.isUnmounted||_.pendingId!==S.suspenseId)return;S.asyncResolved=!0;const{vnode:k}=S;Za(S,P),R&&(k.el=R);const N=!R&&S.subTree.el;A(S,k,p(R||S.subTree.el),R?null:v(S.subTree),_,s,I),N&&(k.placeholder=null,b(N)),Os(S,k.el),C&&--_.deps===0&&_.resolve()})},unmount(S,A){_.isUnmounted=!0,_.activeBranch&&h(_.activeBranch,r,S,A),_.pendingBranch&&h(_.pendingBranch,r,S,A)}};return _}function j0(e,t,r,n,i,o,s,a,u){const c=t.suspense=Dv(t,n,r,e.parentNode,document.createElement("div"),null,i,o,s,a,!0),l=u(e,c.pendingBranch=t.ssContent,r,c,o,s);return c.deps===0&&c.resolve(!1,!0),l}function q0(e){const{shapeFlag:t,children:r}=e,n=t&32;e.ssContent=vc(n?r.default:r),e.ssFallback=n?vc(r.fallback):Fe(Xe)}function vc(e){let t;if(he(e)){const r=qn&&e._c;r&&(e._d=!1,vt()),e=e(),r&&(e._d=!0,t=bt,Fv())}return ve(e)&&(e=A0(e)),e=Tt(e),t&&!e.dynamicChildren&&(e.dynamicChildren=t.filter(r=>r!==e)),e}function Uv(e,t){t&&t.pendingBranch?ve(e)?t.effects.push(...e):t.effects.push(e):Wa(e)}function Ln(e,t){e.activeBranch=t;const{vnode:r,parentComponent:n}=e;let i=t.el;for(;!i&&t.component;)t=t.component.subTree,i=t.el;r.el=i,n&&n.subTree===r&&(n.vnode.el=i,Os(n,i))}function V0(e){const t=e.props&&e.props.suspensible;return t!=null&&t!==!1}const ht=Symbol.for("v-fgt"),nn=Symbol.for("v-txt"),Xe=Symbol.for("v-cmt"),No=Symbol.for("v-stc"),Ci=[];let bt=null;function vt(e=!1){Ci.push(bt=e?null:[])}function Fv(){Ci.pop(),bt=Ci[Ci.length-1]||null}let qn=1;function as(e,t=!1){qn+=e,e<0&&bt&&t&&(bt.hasOnce=!0)}function Hv(e){return e.dynamicChildren=qn>0?bt||Cn:null,Fv(),qn>0&&bt&&bt.push(e),e}function Xr(e,t,r,n,i,o){return Hv(Mn(e,t,r,n,i,o,!0))}function Yr(e,t,r,n,i){return Hv(Fe(e,t,r,n,i,!0))}function Vn(e){return e?e.__v_isVNode===!0:!1}function Ht(e,t){return e.type===t.type&&e.key===t.key}const Bv=({key:e})=>e??null,Lo=({ref:e,ref_key:t,ref_for:r})=>(typeof e=="number"&&(e=""+e),e!=null?He(e)||qe(e)||he(e)?{i:At,r:e,k:t,f:!!r}:e:null);function Mn(e,t=null,r=null,n=0,i=null,o=e===ht?0:1,s=!1,a=!1){const u={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&Bv(t),ref:t&&Lo(t),scopeId:tv,slotScopeIds:null,children:r,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:n,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:At};return a?(hl(u,r),o&128&&e.normalize(u)):r&&(u.shapeFlag|=He(r)?8:16),qn>0&&!s&&bt&&(u.patchFlag>0||o&6)&&u.patchFlag!==32&&bt.push(u),u}const Fe=$0;function $0(e,t=null,r=null,n=0,i=null,o=!1){if((!e||e===gv)&&(e=Xe),Vn(e)){const a=yr(e,t,!0);return r&&hl(a,r),qn>0&&!o&&bt&&(a.shapeFlag&6?bt[bt.indexOf(e)]=a:bt.push(a)),a.patchFlag=-2,a}if(Z0(e)&&(e=e.__vccOpts),t){t=jv(t);let{class:a,style:u}=t;a&&!He(a)&&(t.class=Is(a)),De(u)&&(Cs(u)&&!ve(u)&&(u=Ye({},u)),t.style=As(u))}const s=He(e)?1:ss(e)?128:nv(e)?64:De(e)?4:he(e)?2:0;return Mn(e,t,r,n,i,s,o,!0)}function jv(e){return e?Cs(e)||Tv(e)?Ye({},e):e:null}function yr(e,t,r=!1,n=!1){const{props:i,ref:o,patchFlag:s,children:a,transition:u}=e,c=t?W0(i||{},t):i,l={__v_isVNode:!0,__v_skip:!0,type:e.type,props:c,key:c&&Bv(c),ref:t&&t.ref?r&&o?ve(o)?o.concat(Lo(t)):[o,Lo(t)]:Lo(t):o,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:a,target:e.target,targetStart:e.targetStart,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ht?s===-1?16:s|16:s,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:u,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&yr(e.ssContent),ssFallback:e.ssFallback&&yr(e.ssFallback),placeholder:e.placeholder,el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce};return u&&n&&jn(l,u.clone(l)),l}function ki(e=" ",t=0){return Fe(nn,null,e,t)}function pc(e="",t=!1){return t?(vt(),Yr(Xe,null,e)):Fe(Xe,null,e)}function Tt(e){return e==null||typeof e=="boolean"?Fe(Xe):ve(e)?Fe(ht,null,e.slice()):Vn(e)?Ar(e):Fe(nn,null,String(e))}function Ar(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:yr(e)}function hl(e,t){let r=0;const{shapeFlag:n}=e;if(t==null)t=null;else if(ve(t))r=16;else if(typeof t=="object")if(n&65){const i=t.default;i&&(i._c&&(i._d=!1),hl(e,i()),i._c&&(i._d=!0));return}else{r=32;const i=t._;!i&&!Tv(t)?t._ctx=At:i===3&&At&&(At.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else he(t)?(t={default:t,_ctx:At},r=32):(t=String(t),n&64?(r=16,t=[ki(t)]):r=8);e.children=t,e.shapeFlag|=r}function W0(...e){const t={};for(let r=0;rnt||At;let us,Qa;{const e=Ts(),t=(r,n)=>{let i;return(i=e[r])||(i=e[r]=[]),i.push(n),o=>{i.length>1?i.forEach(s=>s(o)):i[0](o)}};us=t("__VUE_INSTANCE_SETTERS__",r=>nt=r),Qa=t("__VUE_SSR_SETTERS__",r=>$n=r)}const eo=e=>{const t=nt;return us(e),e.scope.on(),()=>{e.scope.off(),us(t)}},gc=()=>{nt&&nt.scope.off(),us(null)};function qv(e){return e.vnode.shapeFlag&4}let $n=!1;function X0(e,t=!1,r=!1){t&&Qa(t);const{props:n,children:i}=e.vnode,o=qv(e);R0(e,n,o,t),N0(e,i,r||t);const s=o?Y0(e,t):void 0;return t&&Qa(!1),s}function Y0(e,t){const r=e.type;e.accessCache=Object.create(null),e.proxy=new Proxy(e.ctx,v0);const{setup:n}=r;if(n){vr();const i=e.setupContext=n.length>1?Q0(e):null,o=eo(e),s=Qi(n,e,0,[e.props,i]),a=Th(s);if(pr(),o(),(a||e.sp)&&!Mr(e)&&cl(e),a){if(s.then(gc,gc),t)return s.then(u=>{Za(e,u)}).catch(u=>{Jn(u,e,0)});e.asyncDep=s}else Za(e,s)}else Vv(e)}function Za(e,t,r){he(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:De(t)&&(e.setupState=Yh(t)),Vv(e)}function Vv(e,t,r){const n=e.type;e.render||(e.render=n.render||Yt);{const i=eo(e);vr();try{p0(e)}finally{pr(),i()}}}const J0={get(e,t){return st(e,"get",""),e[t]}};function Q0(e){const t=r=>{e.exposed=r||{}};return{attrs:new Proxy(e.attrs,J0),slots:e.slots,emit:e.emit,expose:t}}function xs(e){return e.exposed?e.exposeProxy||(e.exposeProxy=new Proxy(Yh(Ny(e.exposed)),{get(t,r){if(r in t)return t[r];if(r in Ii)return Ii[r](e)},has(t,r){return r in t||r in Ii}})):e.proxy}function eu(e,t=!0){return he(e)?e.displayName||e.name:e.name||t&&e.__name}function Z0(e){return he(e)&&"__vccOpts"in e}const We=(e,t)=>jy(e,t,$n);function Et(e,t,r){try{as(-1);const n=arguments.length;return n===2?De(t)&&!ve(t)?Vn(t)?Fe(e,null,[t]):Fe(e,t):Fe(e,null,t):(n>3?r=Array.prototype.slice.call(arguments,2):n===3&&Vn(r)&&(r=[r]),Fe(e,t,r))}finally{as(1)}}const eb="3.5.27";let tu;const mc=typeof window<"u"&&window.trustedTypes;if(mc)try{tu=mc.createPolicy("vue",{createHTML:e=>e})}catch{}const $v=tu?e=>tu.createHTML(e):e=>e,tb="http://www.w3.org/2000/svg",rb="http://www.w3.org/1998/Math/MathML",sr=typeof document<"u"?document:null,yc=sr&&sr.createElement("template"),nb={insert:(e,t,r)=>{t.insertBefore(e,r||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,r,n)=>{const i=t==="svg"?sr.createElementNS(tb,e):t==="mathml"?sr.createElementNS(rb,e):r?sr.createElement(e,{is:r}):sr.createElement(e);return e==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:e=>sr.createTextNode(e),createComment:e=>sr.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>sr.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,r,n,i,o){const s=r?r.previousSibling:t.lastChild;if(i&&(i===o||i.nextSibling))for(;t.insertBefore(i.cloneNode(!0),r),!(i===o||!(i=i.nextSibling)););else{yc.innerHTML=$v(n==="svg"?`${e}`:n==="mathml"?`${e}`:e);const a=yc.content;if(n==="svg"||n==="mathml"){const u=a.firstChild;for(;u.firstChild;)a.appendChild(u.firstChild);a.removeChild(u)}t.insertBefore(a,r)}return[s?s.nextSibling:t.firstChild,r?r.previousSibling:t.lastChild]}},Er="transition",oi="animation",Wi=Symbol("_vtc"),Wv={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},ib=Ye({},iv,Wv),ob=e=>(e.displayName="Transition",e.props=ib,e),sb=ob((e,{slots:t})=>Et(Zy,ab(e),t)),jr=(e,t=[])=>{ve(e)?e.forEach(r=>r(...t)):e&&e(...t)},bc=e=>e?ve(e)?e.some(t=>t.length>1):e.length>1:!1;function ab(e){const t={};for(const k in e)k in Wv||(t[k]=e[k]);if(e.css===!1)return t;const{name:r="v",type:n,duration:i,enterFromClass:o=`${r}-enter-from`,enterActiveClass:s=`${r}-enter-active`,enterToClass:a=`${r}-enter-to`,appearFromClass:u=o,appearActiveClass:c=s,appearToClass:l=a,leaveFromClass:f=`${r}-leave-from`,leaveActiveClass:d=`${r}-leave-active`,leaveToClass:h=`${r}-leave-to`}=e,v=ub(i),p=v&&v[0],b=v&&v[1],{onBeforeEnter:E,onEnter:m,onEnterCancelled:g,onLeave:y,onLeaveCancelled:_,onBeforeAppear:S=E,onAppear:A=m,onAppearCancelled:I=g}=t,C=(k,N,$,ee)=>{k._enterCancelled=ee,qr(k,N?l:a),qr(k,N?c:s),$&&$()},R=(k,N)=>{k._isLeaving=!1,qr(k,f),qr(k,h),qr(k,d),N&&N()},P=k=>(N,$)=>{const ee=k?A:m,B=()=>C(N,k,$);jr(ee,[N,B]),Ec(()=>{qr(N,k?u:o),nr(N,k?l:a),bc(ee)||_c(N,n,p,B)})};return Ye(t,{onBeforeEnter(k){jr(E,[k]),nr(k,o),nr(k,s)},onBeforeAppear(k){jr(S,[k]),nr(k,u),nr(k,c)},onEnter:P(!1),onAppear:P(!0),onLeave(k,N){k._isLeaving=!0;const $=()=>R(k,N);nr(k,f),k._enterCancelled?(nr(k,d),Tc(k)):(Tc(k),nr(k,d)),Ec(()=>{k._isLeaving&&(qr(k,f),nr(k,h),bc(y)||_c(k,n,b,$))}),jr(y,[k,$])},onEnterCancelled(k){C(k,!1,void 0,!0),jr(g,[k])},onAppearCancelled(k){C(k,!0,void 0,!0),jr(I,[k])},onLeaveCancelled(k){R(k),jr(_,[k])}})}function ub(e){if(e==null)return null;if(De(e))return[na(e.enter),na(e.leave)];{const t=na(e);return[t,t]}}function na(e){return kh(e)}function nr(e,t){t.split(/\s+/).forEach(r=>r&&e.classList.add(r)),(e[Wi]||(e[Wi]=new Set)).add(t)}function qr(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.remove(n));const r=e[Wi];r&&(r.delete(t),r.size||(e[Wi]=void 0))}function Ec(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let lb=0;function _c(e,t,r,n){const i=e._endId=++lb,o=()=>{i===e._endId&&n()};if(r!=null)return setTimeout(o,r);const{type:s,timeout:a,propCount:u}=cb(e,t);if(!s)return n();const c=s+"end";let l=0;const f=()=>{e.removeEventListener(c,d),o()},d=h=>{h.target===e&&++l>=u&&f()};setTimeout(()=>{l(r[v]||"").split(", "),i=n(`${Er}Delay`),o=n(`${Er}Duration`),s=Sc(i,o),a=n(`${oi}Delay`),u=n(`${oi}Duration`),c=Sc(a,u);let l=null,f=0,d=0;t===Er?s>0&&(l=Er,f=s,d=o.length):t===oi?c>0&&(l=oi,f=c,d=u.length):(f=Math.max(s,c),l=f>0?s>c?Er:oi:null,d=l?l===Er?o.length:u.length:0);const h=l===Er&&/\b(?:transform|all)(?:,|$)/.test(n(`${Er}Property`).toString());return{type:l,timeout:f,propCount:d,hasTransform:h}}function Sc(e,t){for(;e.lengthwc(r)+wc(e[n])))}function wc(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function Tc(e){return(e?e.ownerDocument:document).body.offsetHeight}function fb(e,t,r){const n=e[Wi];n&&(t=(t?[t,...n]:[...n]).join(" ")),t==null?e.removeAttribute("class"):r?e.setAttribute("class",t):e.className=t}const Ac=Symbol("_vod"),db=Symbol("_vsh"),hb=Symbol(""),vb=/(?:^|;)\s*display\s*:/;function pb(e,t,r){const n=e.style,i=He(r);let o=!1;if(r&&!i){if(t)if(He(t))for(const s of t.split(";")){const a=s.slice(0,s.indexOf(":")).trim();r[a]==null&&Mo(n,a,"")}else for(const s in t)r[s]==null&&Mo(n,s,"");for(const s in r)s==="display"&&(o=!0),Mo(n,s,r[s])}else if(i){if(t!==r){const s=n[hb];s&&(r+=";"+s),n.cssText=r,o=vb.test(r)}}else t&&e.removeAttribute("style");Ac in e&&(e[Ac]=o?n.display:"",e[db]&&(n.display="none"))}const Ic=/\s*!important$/;function Mo(e,t,r){if(ve(r))r.forEach(n=>Mo(e,t,n));else if(r==null&&(r=""),t.startsWith("--"))e.setProperty(t,r);else{const n=gb(e,t);Ic.test(r)?e.setProperty(cn(n),r.replace(Ic,""),"important"):e[n]=r}}const Cc=["Webkit","Moz","ms"],ia={};function gb(e,t){const r=ia[t];if(r)return r;let n=Lt(t);if(n!=="filter"&&n in e)return ia[t]=n;n=ws(n);for(let i=0;ioa||(Eb.then(()=>oa=0),oa=Date.now());function Sb(e,t){const r=n=>{if(!n._vts)n._vts=Date.now();else if(n._vts<=r.attached)return;qt(wb(n,r.value),t,5,[n])};return r.value=e,r.attached=_b(),r}function wb(e,t){if(ve(t)){const r=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{r.call(e),e._stopped=!0},t.map(n=>i=>!i._stopped&&n&&n(i))}else return t}const Nc=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&e.charCodeAt(2)>96&&e.charCodeAt(2)<123,Tb=(e,t,r,n,i,o)=>{const s=i==="svg";t==="class"?fb(e,n,s):t==="style"?pb(e,r,n):Yi(t)?Ju(t)||yb(e,t,r,n,o):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):Ab(e,t,n,s))?(Pc(e,t,n),!e.tagName.includes("-")&&(t==="value"||t==="checked"||t==="selected")&&Rc(e,t,n,s,o,t!=="value")):e._isVueCE&&(/[A-Z]/.test(t)||!He(n))?Pc(e,Lt(t),n,o,t):(t==="true-value"?e._trueValue=n:t==="false-value"&&(e._falseValue=n),Rc(e,t,n,s))};function Ab(e,t,r,n){if(n)return!!(t==="innerHTML"||t==="textContent"||t in e&&Nc(t)&&he(r));if(t==="spellcheck"||t==="draggable"||t==="translate"||t==="autocorrect"||t==="sandbox"&&e.tagName==="IFRAME"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA")return!1;if(t==="width"||t==="height"){const i=e.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return Nc(t)&&He(r)?!1:t in e}const Lc=e=>{const t=e.props["onUpdate:modelValue"]||!1;return ve(t)?r=>Rn(t,r):t};function Ib(e){e.target.composing=!0}function Mc(e){const t=e.target;t.composing&&(t.composing=!1,t.dispatchEvent(new Event("input")))}const sa=Symbol("_assign");function Dc(e,t,r){return t&&(e=e.trim()),r&&(e=Zu(e)),e}const Xx={created(e,{modifiers:{lazy:t,trim:r,number:n}},i){e[sa]=Lc(i);const o=n||i.props&&i.props.type==="number";En(e,t?"change":"input",s=>{s.target.composing||e[sa](Dc(e.value,r,o))}),(r||o)&&En(e,"change",()=>{e.value=Dc(e.value,r,o)}),t||(En(e,"compositionstart",Ib),En(e,"compositionend",Mc),En(e,"change",Mc))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,oldValue:r,modifiers:{lazy:n,trim:i,number:o}},s){if(e[sa]=Lc(s),e.composing)return;const a=(o||e.type==="number")&&!/^0\d/.test(e.value)?Zu(e.value):e.value,u=t??"";a!==u&&(document.activeElement===e&&e.type!=="range"&&(n&&t===r||i&&e.value.trim()===u)||(e.value=u))}},Cb=["ctrl","shift","alt","meta"],kb={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Cb.some(r=>e[`${r}Key`]&&!t.includes(r))},Yx=(e,t)=>{const r=e._withMods||(e._withMods={}),n=t.join(".");return r[n]||(r[n]=((i,...o)=>{for(let s=0;s{const t=Rb().createApp(...e),{mount:r}=t;return t.mount=n=>{const i=zv(n);if(!i)return;const o=t._component;!he(o)&&!o.render&&!o.template&&(o.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const s=r(i,!1,Kv(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),s},t}),xb=((...e)=>{const t=Pb().createApp(...e),{mount:r}=t;return t.mount=n=>{const i=zv(n);if(i)return r(i,!0,Kv(i))},t});function Kv(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function zv(e){return He(e)?document.querySelector(e):e}const Nb=/"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/,Lb=/"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/,Mb=/^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;function Db(e,t){if(e==="__proto__"||e==="constructor"&&t&&typeof t=="object"&&"prototype"in t){Ub(e);return}return t}function Ub(e){console.warn(`[destr] Dropping "${e}" key to prevent prototype pollution.`)}function ls(e,t={}){if(typeof e!="string")return e;if(e[0]==='"'&&e[e.length-1]==='"'&&e.indexOf("\\")===-1)return e.slice(1,-1);const r=e.trim();if(r.length<=9)switch(r.toLowerCase()){case"true":return!0;case"false":return!1;case"undefined":return;case"null":return null;case"nan":return Number.NaN;case"infinity":return Number.POSITIVE_INFINITY;case"-infinity":return Number.NEGATIVE_INFINITY}if(!Mb.test(e)){if(t.strict)throw new SyntaxError("[destr] Invalid JSON");return e}try{if(Nb.test(e)||Lb.test(e)){if(t.strict)throw new Error("[destr] Possible prototype pollution");return JSON.parse(e,Db)}return JSON.parse(e)}catch(n){if(t.strict)throw n;return e}}const Fb=/#/g,Hb=/&/g,Bb=/\//g,jb=/=/g,vl=/\+/g,qb=/%5e/gi,Vb=/%60/gi,$b=/%7c/gi,Wb=/%20/gi,Gb=/%2f/gi;function Kb(e){return encodeURI(""+e).replace($b,"|")}function ru(e){return Kb(typeof e=="string"?e:JSON.stringify(e)).replace(vl,"%2B").replace(Wb,"+").replace(Fb,"%23").replace(Hb,"%26").replace(Vb,"`").replace(qb,"^").replace(Bb,"%2F")}function aa(e){return ru(e).replace(jb,"%3D")}function Gi(e=""){try{return decodeURIComponent(""+e)}catch{return""+e}}function Fc(e){return Gi(e.replace(Gb,"%252F"))}function zb(e){return Gi(e.replace(vl," "))}function Xb(e){return Gi(e.replace(vl," "))}function pl(e=""){const t=Object.create(null);e[0]==="?"&&(e=e.slice(1));for(const r of e.split("&")){const n=r.match(/([^=]+)=?(.*)/)||[];if(n.length<2)continue;const i=zb(n[1]);if(i==="__proto__"||i==="constructor")continue;const o=Xb(n[2]||"");t[i]===void 0?t[i]=o:Array.isArray(t[i])?t[i].push(o):t[i]=[t[i],o]}return t}function Yb(e,t){return(typeof t=="number"||typeof t=="boolean")&&(t=String(t)),t?Array.isArray(t)?t.map(r=>`${aa(e)}=${ru(r)}`).join("&"):`${aa(e)}=${ru(t)}`:aa(e)}function Jb(e){return Object.keys(e).filter(t=>e[t]!==void 0).map(t=>Yb(t,e[t])).filter(Boolean).join("&")}const Qb=/^[\s\w\0+.-]{2,}:([/\\]{1,2})/,Zb=/^[\s\w\0+.-]{2,}:([/\\]{2})?/,eE=/^([/\\]\s*){2,}[^/\\]/,tE=/^[\s\0]*(blob|data|javascript|vbscript):$/i,rE=/\/$|\/\?|\/#/,nE=/^\.?\//;function Hr(e,t={}){return typeof t=="boolean"&&(t={acceptRelative:t}),t.strict?Qb.test(e):Zb.test(e)||(t.acceptRelative?eE.test(e):!1)}function iE(e){return!!e&&tE.test(e)}function nu(e="",t){return t?rE.test(e):e.endsWith("/")}function Ki(e="",t){if(!t)return(nu(e)?e.slice(0,-1):e)||"/";if(!nu(e,!0))return e||"/";let r=e,n="";const i=e.indexOf("#");i!==-1&&(r=e.slice(0,i),n=e.slice(i));const[o,...s]=r.split("?");return((o.endsWith("/")?o.slice(0,-1):o)||"/")+(s.length>0?`?${s.join("?")}`:"")+n}function Xv(e="",t){if(!t)return e.endsWith("/")?e:e+"/";if(nu(e,!0))return e||"/";let r=e,n="";const i=e.indexOf("#");if(i!==-1&&(r=e.slice(0,i),n=e.slice(i),!r))return n;const[o,...s]=r.split("?");return o+"/"+(s.length>0?`?${s.join("?")}`:"")+n}function oE(e,t){if(Jv(t)||Hr(e))return e;const r=Ki(t);if(e.startsWith(r)){const n=e[r.length];if(!n||n==="/"||n==="?")return e}return gl(r,e)}function Hc(e,t){if(Jv(t))return e;const r=Ki(t);if(!e.startsWith(r))return e;const n=e[r.length];if(n&&n!=="/"&&n!=="?")return e;const i=e.slice(r.length);return i[0]==="/"?i:"/"+i}function Yv(e,t){const r=ep(e),n={...pl(r.search),...t};return r.search=Jb(n),uE(r)}function Jv(e){return!e||e==="/"}function sE(e){return e&&e!=="/"}function gl(e,...t){let r=e||"";for(const n of t.filter(i=>sE(i)))if(r){const i=n.replace(nE,"");r=Xv(r)+i}else r=n;return r}function Qv(...e){const t=/\/(?!\/)/,r=e.filter(Boolean),n=[];let i=0;for(const s of r)if(!(!s||s==="/")){for(const[a,u]of s.split(t).entries())if(!(!u||u===".")){if(u===".."){if(n.length===1&&Hr(n[0]))continue;n.pop(),i--;continue}if(a===1&&n[n.length-1]?.endsWith(":/")){n[n.length-1]+="/"+u;continue}n.push(u),i++}}let o=n.join("/");return i>=0?r[0]?.startsWith("/")&&!o.startsWith("/")?o="/"+o:r[0]?.startsWith("./")&&!o.startsWith("./")&&(o="./"+o):o="../".repeat(-1*i)+o,r[r.length-1]?.endsWith("/")&&!o.endsWith("/")&&(o+="/"),o}function aE(e,t){return Gi(Ki(e))===Gi(Ki(t))}const Zv=Symbol.for("ufo:protocolRelative");function ep(e="",t){const r=e.match(/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i);if(r){const[,f,d=""]=r;return{protocol:f.toLowerCase(),pathname:d,href:f+d,auth:"",host:"",search:"",hash:""}}if(!Hr(e,{acceptRelative:!0}))return Bc(e);const[,n="",i,o=""]=e.replace(/\\/g,"/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/)||[];let[,s="",a=""]=o.match(/([^#/?]*)(.*)?/)||[];n==="file:"&&(a=a.replace(/\/(?=[A-Za-z]:)/,""));const{pathname:u,search:c,hash:l}=Bc(a);return{protocol:n.toLowerCase(),auth:i?i.slice(0,Math.max(0,i.length-1)):"",host:s,pathname:u,search:c,hash:l,[Zv]:!n}}function Bc(e=""){const[t="",r="",n=""]=(e.match(/([^#?]*)(\?[^#]*)?(#.*)?/)||[]).splice(1);return{pathname:t,search:r,hash:n}}function uE(e){const t=e.pathname||"",r=e.search?(e.search.startsWith("?")?"":"?")+e.search:"",n=e.hash||"",i=e.auth?e.auth+"@":"",o=e.host||"";return(e.protocol||e[Zv]?(e.protocol||"")+"//":"")+i+o+t+r+n}let lE=class extends Error{constructor(t,r){super(t,r),this.name="FetchError",r?.cause&&!this.cause&&(this.cause=r.cause)}};function cE(e){const t=e.error?.message||e.error?.toString()||"",r=e.request?.method||e.options?.method||"GET",n=e.request?.url||String(e.request)||"/",i=`[${r}] ${JSON.stringify(n)}`,o=e.response?`${e.response.status} ${e.response.statusText}`:"",s=`${i}: ${o}${t?` ${t}`:""}`,a=new lE(s,e.error?{cause:e.error}:void 0);for(const u of["request","options","response"])Object.defineProperty(a,u,{get(){return e[u]}});for(const[u,c]of[["data","_data"],["status","status"],["statusCode","status"],["statusText","statusText"],["statusMessage","statusText"]])Object.defineProperty(a,u,{get(){return e.response&&e.response[c]}});return a}const fE=new Set(Object.freeze(["PATCH","POST","PUT","DELETE"]));function jc(e="GET"){return fE.has(e.toUpperCase())}function dE(e){if(e===void 0)return!1;const t=typeof e;return t==="string"||t==="number"||t==="boolean"||t===null?!0:t!=="object"?!1:Array.isArray(e)?!0:e.buffer||e instanceof FormData||e instanceof URLSearchParams?!1:e.constructor&&e.constructor.name==="Object"||typeof e.toJSON=="function"}const hE=new Set(["image/svg","application/xml","application/xhtml","application/html"]),vE=/^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;function pE(e=""){if(!e)return"json";const t=e.split(";").shift()||"";return vE.test(t)?"json":t==="text/event-stream"?"stream":hE.has(t)||t.startsWith("text/")?"text":"blob"}function gE(e,t,r,n){const i=mE(t?.headers??e?.headers,r?.headers,n);let o;return(r?.query||r?.params||t?.params||t?.query)&&(o={...r?.params,...r?.query,...t?.params,...t?.query}),{...r,...t,query:o,params:o,headers:i}}function mE(e,t,r){if(!t)return new r(e);const n=new r(t);if(e)for(const[i,o]of Symbol.iterator in e||Array.isArray(e)?e:new r(e))n.set(i,o);return n}async function po(e,t){if(t)if(Array.isArray(t))for(const r of t)await r(e);else await t(e)}const yE=new Set([408,409,425,429,500,502,503,504]),bE=new Set([101,204,205,304]);function tp(e={}){const{fetch:t=globalThis.fetch,Headers:r=globalThis.Headers,AbortController:n=globalThis.AbortController}=e;async function i(a){const u=a.error&&a.error.name==="AbortError"&&!a.options.timeout||!1;if(a.options.retry!==!1&&!u){let l;typeof a.options.retry=="number"?l=a.options.retry:l=jc(a.options.method)?0:1;const f=a.response&&a.response.status||500;if(l>0&&(Array.isArray(a.options.retryStatusCodes)?a.options.retryStatusCodes.includes(f):yE.has(f))){const d=typeof a.options.retryDelay=="function"?a.options.retryDelay(a):a.options.retryDelay||0;return d>0&&await new Promise(h=>setTimeout(h,d)),o(a.request,{...a.options,retry:l-1})}}const c=cE(a);throw Error.captureStackTrace&&Error.captureStackTrace(c,o),c}const o=async function(u,c={}){const l={request:u,options:gE(u,c,e.defaults,r),response:void 0,error:void 0};if(l.options.method&&(l.options.method=l.options.method.toUpperCase()),l.options.onRequest&&(await po(l,l.options.onRequest),l.options.headers instanceof r||(l.options.headers=new r(l.options.headers||{}))),typeof l.request=="string"&&(l.options.baseURL&&(l.request=oE(l.request,l.options.baseURL)),l.options.query&&(l.request=Yv(l.request,l.options.query),delete l.options.query),"query"in l.options&&delete l.options.query,"params"in l.options&&delete l.options.params),l.options.body&&jc(l.options.method))if(dE(l.options.body)){const h=l.options.headers.get("content-type");typeof l.options.body!="string"&&(l.options.body=h==="application/x-www-form-urlencoded"?new URLSearchParams(l.options.body).toString():JSON.stringify(l.options.body)),h||l.options.headers.set("content-type","application/json"),l.options.headers.has("accept")||l.options.headers.set("accept","application/json")}else("pipeTo"in l.options.body&&typeof l.options.body.pipeTo=="function"||typeof l.options.body.pipe=="function")&&("duplex"in l.options||(l.options.duplex="half"));let f;if(!l.options.signal&&l.options.timeout){const h=new n;f=setTimeout(()=>{const v=new Error("[TimeoutError]: The operation was aborted due to timeout");v.name="TimeoutError",v.code=23,h.abort(v)},l.options.timeout),l.options.signal=h.signal}try{l.response=await t(l.request,l.options)}catch(h){return l.error=h,l.options.onRequestError&&await po(l,l.options.onRequestError),await i(l)}finally{f&&clearTimeout(f)}if((l.response.body||l.response._bodyInit)&&!bE.has(l.response.status)&&l.options.method!=="HEAD"){const h=(l.options.parseResponse?"json":l.options.responseType)||pE(l.response.headers.get("content-type")||"");switch(h){case"json":{const v=await l.response.text(),p=l.options.parseResponse||ls;l.response._data=p(v);break}case"stream":{l.response._data=l.response.body||l.response._bodyInit;break}default:l.response._data=await l.response[h]()}}return l.options.onResponse&&await po(l,l.options.onResponse),!l.options.ignoreResponseError&&l.response.status>=400&&l.response.status<600?(l.options.onResponseError&&await po(l,l.options.onResponseError),await i(l)):l.response},s=async function(u,c){return(await o(u,c))._data};return s.raw=o,s.native=(...a)=>t(...a),s.create=(a={},u={})=>tp({...e,...u,defaults:{...e.defaults,...u.defaults,...a}}),s}const cs=(function(){if(typeof globalThis<"u")return globalThis;if(typeof self<"u")return self;if(typeof window<"u")return window;if(typeof global<"u")return global;throw new Error("unable to locate global object")})(),EE=cs.fetch?(...e)=>cs.fetch(...e):()=>Promise.reject(new Error("[ofetch] global.fetch is not supported!")),_E=cs.Headers,SE=cs.AbortController,wE=tp({fetch:EE,Headers:_E,AbortController:SE}),TE=wE,AE=()=>window?.__NUXT__?.config||window?.useNuxtApp?.().payload?.config,ml=()=>AE().app,IE=()=>ml().baseURL,CE=()=>ml().buildAssetsDir,yl=(...e)=>Qv(rp(),CE(),...e),rp=(...e)=>{const t=ml(),r=t.cdnURL||t.baseURL;return e.length?Qv(r,...e):r};globalThis.__buildAssetsURL=yl,globalThis.__publicAssetsURL=rp;globalThis.$fetch||(globalThis.$fetch=TE.create({baseURL:IE()}));"global"in globalThis||(globalThis.global=globalThis);function iu(e,t={},r){for(const n in e){const i=e[n],o=r?`${r}:${n}`:n;typeof i=="object"&&i!==null?iu(i,t,o):typeof i=="function"&&(t[o]=i)}return t}const kE={run:e=>e()},RE=()=>kE,np=typeof console.createTask<"u"?console.createTask:RE;function PE(e,t){const r=t.shift(),n=np(r);return e.reduce((i,o)=>i.then(()=>n.run(()=>o(...t))),Promise.resolve())}function OE(e,t){const r=t.shift(),n=np(r);return Promise.all(e.map(i=>n.run(()=>i(...t))))}function ua(e,t){for(const r of[...e])r(t)}let xE=class{constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(t,r,n={}){if(!t||typeof r!="function")return()=>{};const i=t;let o;for(;this._deprecatedHooks[t];)o=this._deprecatedHooks[t],t=o.to;if(o&&!n.allowDeprecated){let s=o.message;s||(s=`${i} hook has been deprecated`+(o.to?`, please use ${o.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(s)||(console.warn(s),this._deprecatedMessages.add(s))}if(!r.name)try{Object.defineProperty(r,"name",{get:()=>"_"+t.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[t]=this._hooks[t]||[],this._hooks[t].push(r),()=>{r&&(this.removeHook(t,r),r=void 0)}}hookOnce(t,r){let n,i=(...o)=>(typeof n=="function"&&n(),n=void 0,i=void 0,r(...o));return n=this.hook(t,i),n}removeHook(t,r){if(this._hooks[t]){const n=this._hooks[t].indexOf(r);n!==-1&&this._hooks[t].splice(n,1),this._hooks[t].length===0&&delete this._hooks[t]}}deprecateHook(t,r){this._deprecatedHooks[t]=typeof r=="string"?{to:r}:r;const n=this._hooks[t]||[];delete this._hooks[t];for(const i of n)this.hook(t,i)}deprecateHooks(t){Object.assign(this._deprecatedHooks,t);for(const r in t)this.deprecateHook(r,t[r])}addHooks(t){const r=iu(t),n=Object.keys(r).map(i=>this.hook(i,r[i]));return()=>{for(const i of n.splice(0,n.length))i()}}removeHooks(t){const r=iu(t);for(const n in r)this.removeHook(n,r[n])}removeAllHooks(){for(const t in this._hooks)delete this._hooks[t]}callHook(t,...r){return r.unshift(t),this.callHookWith(PE,t,...r)}callHookParallel(t,...r){return r.unshift(t),this.callHookWith(OE,t,...r)}callHookWith(t,r,...n){const i=this._before||this._after?{name:r,args:n,context:{}}:void 0;this._before&&ua(this._before,i);const o=t(r in this._hooks?[...this._hooks[r]]:[],n);return o instanceof Promise?o.finally(()=>{this._after&&i&&ua(this._after,i)}):(this._after&&i&&ua(this._after,i),o)}beforeEach(t){return this._before=this._before||[],this._before.push(t),()=>{if(this._before!==void 0){const r=this._before.indexOf(t);r!==-1&&this._before.splice(r,1)}}}afterEach(t){return this._after=this._after||[],this._after.push(t),()=>{if(this._after!==void 0){const r=this._after.indexOf(t);r!==-1&&this._after.splice(r,1)}}}};function NE(){return new xE}function LE(e={}){let t,r=!1;const n=s=>{if(t&&t!==s)throw new Error("Context conflict")};let i;if(e.asyncContext){const s=e.AsyncLocalStorage||globalThis.AsyncLocalStorage;s?i=new s:console.warn("[unctx] `AsyncLocalStorage` is not provided.")}const o=()=>{if(i){const s=i.getStore();if(s!==void 0)return s}return t};return{use:()=>{const s=o();if(s===void 0)throw new Error("Context is not available");return s},tryUse:()=>o(),set:(s,a)=>{a||n(s),t=s,r=!0},unset:()=>{t=void 0,r=!1},call:(s,a)=>{n(s),t=s;try{return i?i.run(s,a):a()}finally{r||(t=void 0)}},async callAsync(s,a){t=s;const u=()=>{t=s},c=()=>t===s?u:void 0;ou.add(c);try{const l=i?i.run(s,a):a();return r||(t=void 0),await l}finally{ou.delete(c)}}}}function ME(e={}){const t={};return{get(r,n={}){return t[r]||(t[r]=LE({...e,...n})),t[r]}}}const fs=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof global<"u"?global:typeof window<"u"?window:{},qc="__unctx__",DE=fs[qc]||(fs[qc]=ME()),UE=(e,t={})=>DE.get(e,t),Vc="__unctx_async_handlers__",ou=fs[Vc]||(fs[Vc]=new Set);function Pi(e){const t=[];for(const i of ou){const o=i();o&&t.push(o)}const r=()=>{for(const i of t)i()};let n=e();return n&&typeof n=="object"&&"catch"in n&&(n=n.catch(i=>{throw r(),i})),[n,r]}const $c=!1,FE=!1,Wc={id:"__nuxt-loader"},HE={componentName:"NuxtLink",prefetch:!0,prefetchOn:{visibility:!0}},BE="#__nuxt",ip="nuxt-app",Gc=36e5,jE="vite:preloadError";function op(e=ip){return UE(e,{asyncContext:!1})}const qE="__nuxt_plugin";function VE(e){let t=0;const r={_id:e.id||ip||"nuxt-app",_scope:hy(),provide:void 0,versions:{get nuxt(){return"4.3.0"},get vue(){return r.vueApp.version}},payload:dr({...e.ssrContext?.payload||{},data:dr({}),state:Fr({}),once:new Set,_errors:dr({})}),static:{data:{}},runWithContext(i){return r._scope.active&&!Lh()?r._scope.run(()=>Kc(r,i)):Kc(r,i)},isHydrating:!0,deferHydration(){if(!r.isHydrating)return()=>{};t++;let i=!1;return()=>{if(!i&&(i=!0,t--,t===0))return r.isHydrating=!1,r.callHook("app:suspense:resolve")}},_asyncDataPromises:{},_asyncData:dr({}),_payloadRevivers:{},...e};{const i=window.__NUXT__;if(i)for(const o in i)switch(o){case"data":case"state":case"_errors":Object.assign(r.payload[o],i[o]);break;default:r.payload[o]=i[o]}}r.hooks=NE(),r.hook=r.hooks.hook,r.callHook=r.hooks.callHook,r.provide=(i,o)=>{const s="$"+i;go(r,s,o),go(r.vueApp.config.globalProperties,s,o)},go(r.vueApp,"$nuxt",r),go(r.vueApp.config.globalProperties,"$nuxt",r);{window.addEventListener(jE,o=>{r.callHook("app:chunkError",{error:o.payload}),o.payload.message.includes("Unable to preload CSS")&&o.preventDefault()}),window.useNuxtApp||=Ve;const i=r.hook("app:error",(...o)=>{console.error("[nuxt] error caught during app initialization",...o)});r.hook("app:mounted",i)}const n=r.payload.config;return r.provide("config",n),r}function $E(e,t){t.hooks&&e.hooks.addHooks(t.hooks)}async function WE(e,t){if(typeof t=="function"){const{provide:r}=await e.runWithContext(()=>t(e))||{};if(r&&typeof r=="object")for(const n in r)e.provide(n,r[n])}}async function GE(e,t){const r=new Set,n=[],i=[];let o,s=0;async function a(u){const c=u.dependsOn?.filter(l=>t.some(f=>f._name===l)&&!r.has(l))??[];if(c.length>0)n.push([new Set(c),u]);else{const l=WE(e,u).then(async()=>{u._name&&(r.add(u._name),await Promise.all(n.map(async([f,d])=>{f.has(u._name)&&(f.delete(u._name),f.size===0&&(s++,await a(d)))})))}).catch(f=>{if(!u.parallel&&!e.payload.error)throw f;o||=f});u.parallel?i.push(l):await l}}for(const u of t)$E(e,u);for(const u of t)await a(u);if(await Promise.all(i),s)for(let u=0;u{}),e,{[qE]:!0,_name:t})}function Kc(e,t,r){const n=()=>t();return op(e._id).set(e),e.vueApp.runWithContext(n)}function KE(e){let t;return ul()&&(t=Zn()?.appContext.app.$nuxt),t||=op(e).tryUse(),t||null}function Ve(e){const t=KE(e);if(!t)throw new Error("[nuxt] instance unavailable");return t}function to(e){return Ve().$config}function go(e,t,r){Object.defineProperty(e,t,{get:()=>r})}function la(e){if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t!==null&&t!==Object.prototype&&Object.getPrototypeOf(t)!==null||Symbol.iterator in e?!1:Symbol.toStringTag in e?Object.prototype.toString.call(e)==="[object Module]":!0}function su(e,t,r=".",n){if(!la(t))return su(e,{},r,n);const i=Object.assign({},t);for(const o in e){if(o==="__proto__"||o==="constructor")continue;const s=e[o];s!=null&&(n&&n(i,o,s,r)||(Array.isArray(s)&&Array.isArray(i[o])?i[o]=[...s,...i[o]]:la(s)&&la(i[o])?i[o]=su(s,i[o],(r?`${r}.`:"")+o.toString(),n):i[o]=s))}return i}function zE(e){return(...t)=>t.reduce((r,n)=>su(r,n,"",e),{})}const sp=zE();function XE(e,t){try{return t in e}catch{return!1}}class zc extends Error{static __h3_error__=!0;statusCode=500;fatal=!1;unhandled=!1;statusMessage;data;cause;constructor(t,r={}){super(t,r),r.cause&&!this.cause&&(this.cause=r.cause)}toJSON(){const t={message:this.message,statusCode:au(this.statusCode,500)};return this.statusMessage&&(t.statusMessage=ap(this.statusMessage)),this.data!==void 0&&(t.data=this.data),t}}function YE(e){if(typeof e=="string")return new zc(e);if(JE(e))return e;const t=new zc(e.message??e.statusMessage??"",{cause:e.cause||e});if(XE(e,"stack"))try{Object.defineProperty(t,"stack",{get(){return e.stack}})}catch{try{t.stack=e.stack}catch{}}if(e.data&&(t.data=e.data),e.statusCode?t.statusCode=au(e.statusCode,t.statusCode):e.status&&(t.statusCode=au(e.status,t.statusCode)),e.statusMessage?t.statusMessage=e.statusMessage:e.statusText&&(t.statusMessage=e.statusText),t.statusMessage){const r=t.statusMessage;ap(t.statusMessage)!==r&&console.warn("[h3] Please prefer using `message` for longer error messages instead of `statusMessage`. In the future, `statusMessage` will be sanitized by default.")}return e.fatal!==void 0&&(t.fatal=e.fatal),e.unhandled!==void 0&&(t.unhandled=e.unhandled),t}function JE(e){return e?.constructor?.__h3_error__===!0}const QE=/[^\u0009\u0020-\u007E]/g;function ap(e=""){return e.replace(QE,"")}function au(e,t=200){return!e||(typeof e=="string"&&(e=Number.parseInt(e,10)),e<100||e>999)?t:e}const ZE=Symbol("layout-meta"),Ns=Symbol("route");import.meta.url.replace(/\/app\/.*$/,"/");const St=()=>Ve()?.$router,e_=()=>ul()?It(Ns,Ve()._route):Ve()._route;function Zx(e){return e}const t_=()=>{try{if(Ve()._processingMiddleware)return!0}catch{return!1}return!1},up=(e,t)=>{e||="/";const r=typeof e=="string"?e:"path"in e?uu(e):St().resolve(e).href;if(t?.open){const{target:u="_blank",windowFeatures:c={}}=t.open,l=[];for(const[f,d]of Object.entries(c))d!==void 0&&l.push(`${f.toLowerCase()}=${d}`);return open(r,u,l.join(", ")),Promise.resolve()}const n=Hr(r,{acceptRelative:!0}),i=t?.external||n;if(i){if(!t?.external)throw new Error("Navigating to an external URL is not allowed by default. Use `navigateTo(url, { external: true })`.");const{protocol:u}=new URL(r,window.location.href);if(u&&iE(u))throw new Error(`Cannot navigate to a URL with '${u}' protocol.`)}const o=t_();if(!i&&o){if(t?.replace){if(typeof e=="string"){const{pathname:u,search:c,hash:l}=ep(e);return{path:u,...c&&{query:pl(c)},...l&&{hash:l},replace:!0}}return{...e,replace:!0}}return e}const s=St(),a=Ve();return i?(a._scope.stop(),t?.replace?location.replace(r):location.href=r,o?a.isHydrating?new Promise(()=>{}):!1:Promise.resolve()):t?.replace?s.replace(e):s.push(e)};function uu(e){return Yv(e.path||"",e.query||{})+(e.hash||"")}const lp="__nuxt_error",Ls=()=>Jh(Ve().payload,"error"),Kr=e=>{const t=on(e);try{const r=Ls();Ve().hooks.callHook("app:error",t),r.value||=t}catch{throw t}return t},r_=async(e={})=>{const t=Ve(),r=Ls();t.callHook("app:error:cleared",e),e.redirect&&await St().replace(e.redirect),r.value=void 0},cp=e=>!!e&&typeof e=="object"&&lp in e,on=e=>{typeof e!="string"&&e.statusText&&(e.message??=e.statusText);const t=YE(e);return Object.defineProperty(t,lp,{value:!0,configurable:!1,writable:!1}),t};function n_(e){const t=o_(e),r=new ArrayBuffer(t.length),n=new DataView(r);for(let i=0;i>16),t+=String.fromCharCode((r&65280)>>8),t+=String.fromCharCode(r&255),r=n=0);return n===12?(r>>=4,t+=String.fromCharCode(r)):n===18&&(r>>=2,t+=String.fromCharCode((r&65280)>>8),t+=String.fromCharCode(r&255)),t}const s_=-1,a_=-2,u_=-3,l_=-4,c_=-5,f_=-6;function d_(e,t){return h_(JSON.parse(e),t)}function h_(e,t){if(typeof e=="number")return o(e,!0);if(!Array.isArray(e)||e.length===0)throw new Error("Invalid input");const r=e,n=Array(r.length);let i=null;function o(s,a=!1){if(s===s_)return;if(s===u_)return NaN;if(s===l_)return 1/0;if(s===c_)return-1/0;if(s===f_)return-0;if(a||typeof s!="number")throw new Error("Invalid input");if(s in n)return n[s];const u=r[s];if(!u||typeof u!="object")n[s]=u;else if(Array.isArray(u))if(typeof u[0]=="string"){const c=u[0],l=t&&Object.hasOwn(t,c)?t[c]:void 0;if(l){let f=u[1];if(typeof f!="number"&&(f=r.push(u[1])-1),i??=new Set,i.has(f))throw new Error("Invalid circular reference");return i.add(f),n[s]=l(o(f)),i.delete(f),n[s]}switch(c){case"Date":n[s]=new Date(u[1]);break;case"Set":const f=new Set;n[s]=f;for(let v=1;v{if(console.createTask)return console.createTask;const e={run:t=>t()};return()=>e})();function dp(e,t,r,n){for(let i=r;ie[i](...t)):e[i](...t);if(o instanceof Promise)return o.then(()=>dp(e,t,i+1,n))}catch(o){return Promise.reject(o)}}function __(e,t,r){if(e.length>0)return dp(e,t,0,fp(r))}function S_(e,t,r){if(e.length>0){const n=fp(r);return Promise.all(e.map(i=>n.run(()=>i(...t))))}}function ca(e,t){for(const r of[...e])r(t)}var w_=class{_hooks;_before;_after;_deprecatedHooks;_deprecatedMessages;constructor(){this._hooks={},this._before=void 0,this._after=void 0,this._deprecatedMessages=void 0,this._deprecatedHooks={},this.hook=this.hook.bind(this),this.callHook=this.callHook.bind(this),this.callHookWith=this.callHookWith.bind(this)}hook(e,t,r={}){if(!e||typeof t!="function")return()=>{};const n=e;let i;for(;this._deprecatedHooks[e];)i=this._deprecatedHooks[e],e=i.to;if(i&&!r.allowDeprecated){let o=i.message;o||(o=`${n} hook has been deprecated`+(i.to?`, please use ${i.to}`:"")),this._deprecatedMessages||(this._deprecatedMessages=new Set),this._deprecatedMessages.has(o)||(console.warn(o),this._deprecatedMessages.add(o))}if(!t.name)try{Object.defineProperty(t,"name",{get:()=>"_"+e.replace(/\W+/g,"_")+"_hook_cb",configurable:!0})}catch{}return this._hooks[e]=this._hooks[e]||[],this._hooks[e].push(t),()=>{t&&(this.removeHook(e,t),t=void 0)}}hookOnce(e,t){let r,n=(...i)=>(typeof r=="function"&&r(),r=void 0,n=void 0,t(...i));return r=this.hook(e,n),r}removeHook(e,t){const r=this._hooks[e];if(r){const n=r.indexOf(t);n!==-1&&r.splice(n,1),r.length===0&&(this._hooks[e]=void 0)}}deprecateHook(e,t){this._deprecatedHooks[e]=typeof t=="string"?{to:t}:t;const r=this._hooks[e]||[];this._hooks[e]=void 0;for(const n of r)this.hook(e,n)}deprecateHooks(e){for(const t in e)this.deprecateHook(t,e[t])}addHooks(e){const t=lu(e),r=Object.keys(t).map(n=>this.hook(n,t[n]));return()=>{for(const n of r)n();r.length=0}}removeHooks(e){const t=lu(e);for(const r in t)this.removeHook(r,t[r])}removeAllHooks(){this._hooks={}}callHook(e,...t){return this.callHookWith(__,e,t)}callHookParallel(e,...t){return this.callHookWith(S_,e,t)}callHookWith(e,t,r){const n=this._before||this._after?{name:t,args:r,context:{}}:void 0;this._before&&ca(this._before,n);const i=e(this._hooks[t]?[...this._hooks[t]]:[],r,t);return i instanceof Promise?i.finally(()=>{this._after&&n&&ca(this._after,n)}):(this._after&&n&&ca(this._after,n),i)}beforeEach(e){return this._before=this._before||[],this._before.push(e),()=>{if(this._before!==void 0){const t=this._before.indexOf(e);t!==-1&&this._before.splice(t,1)}}}afterEach(e){return this._after=this._after||[],this._after.push(e),()=>{if(this._after!==void 0){const t=this._after.indexOf(e);t!==-1&&this._after.splice(t,1)}}}};function T_(){return new w_}const A_=["name","property","http-equiv"],I_=new Set(["viewport","description","keywords","robots"]);function hp(e){const t=e.split(":");return t.length?E_.has(t[1]):!1}function cu(e){const{props:t,tag:r}=e;if(m_.has(r))return r;if(r==="link"&&t.rel==="canonical")return"canonical";if(t.charset)return"charset";if(e.tag==="meta"){for(const n of A_)if(t[n]!==void 0){const i=t[n],o=i&&typeof i=="string"&&i.includes(":"),s=i&&I_.has(i),u=!(o||s)&&e.key?`:key:${e.key}`:"";return`${r}:${i}${u}`}}if(e.key)return`${r}:key:${e.key}`;if(t.id)return`${r}:id:${t.id}`;if(p_.has(r)){const n=e.textContent||e.innerHTML;if(n)return`${r}:content:${n}`}}function Yc(e){const t=e._h||e._d;if(t)return t;const r=e.textContent||e.innerHTML;return r||`${e.tag}:${Object.entries(e.props).map(([n,i])=>`${n}:${String(i)}`).join(",")}`}function ds(e,t,r){typeof e==="function"&&(!r||r!=="titleTemplate"&&!(r[0]==="o"&&r[1]==="n"))&&(e=e());const i=t?t(r,e):e;if(Array.isArray(i))return i.map(o=>ds(o,t));if(i?.constructor===Object){const o={};for(const s of Object.keys(i))o[s]=ds(i[s],t,s);return o}return i}function C_(e,t){const r=e==="style"?new Map:new Set;function n(i){if(i==null||i===void 0)return;const o=String(i).trim();if(o)if(e==="style"){const[s,...a]=o.split(":").map(u=>u?u.trim():"");s&&a.length&&r.set(s,a.join(":"))}else o.split(" ").filter(Boolean).forEach(s=>r.add(s))}return typeof t=="string"?e==="style"?t.split(";").forEach(n):n(t):Array.isArray(t)?t.forEach(i=>n(i)):t&&typeof t=="object"&&Object.entries(t).forEach(([i,o])=>{o&&o!=="false"&&(e==="style"?r.set(String(i).trim(),String(o)):n(i))}),r}function vp(e,t){return e.props=e.props||{},t?e.tag==="templateParams"?(e.props=t,e):(Object.entries(t).forEach(([r,n])=>{if(n===null){e.props[r]=null;return}if(r==="class"||r==="style"){e.props[r]=C_(r,n);return}if(y_.has(r)){if(["textContent","innerHTML"].includes(r)&&typeof n=="object"){let a=t.type;if(t.type||(a="application/json"),!a?.endsWith("json")&&a!=="speculationrules")return;t.type=a,e.props.type=a,e[r]=JSON.stringify(n)}else e[r]=n;return}const i=String(n),o=r.startsWith("data-"),s=e.tag==="meta"&&r==="content";i==="true"||i===""?e.props[r]=o||s?i:!0:!n&&o&&i==="false"?e.props[r]="false":n!==void 0&&(e.props[r]=n)}),e):e}function k_(e,t){const r=typeof t=="object"&&typeof t!="function"?t:{[e==="script"||e==="noscript"||e==="style"?"innerHTML":"textContent"]:t},n=vp({tag:e,props:{}},r);return n.key&&v_.has(n.tag)&&(n.props["data-hid"]=n._h=n.key),n.tag==="script"&&typeof n.innerHTML=="object"&&(n.innerHTML=JSON.stringify(n.innerHTML),n.props.type=n.props.type||"application/json"),Array.isArray(n.props.content)?n.props.content.map(i=>({...n,props:{...n.props,content:i}})):n}function R_(e,t){if(!e)return[];typeof e=="function"&&(e=e());const r=(i,o)=>{for(let s=0;s{if(o!==void 0)for(const s of Array.isArray(o)?o:[o])n.push(k_(i,s))}),n.flat()}const Jc=(e,t)=>e._w===t._w?e._p-t._p:e._w-t._w,Qc={base:-10,title:10},P_={critical:-8,high:-1,low:2},Zc={meta:{"content-security-policy":-30,charset:-20,viewport:-15},link:{preconnect:20,stylesheet:60,preload:70,modulepreload:70,prefetch:90,"dns-prefetch":90,prerender:90},script:{async:30,defer:80,sync:50},style:{imported:40,sync:60}},O_=/@import/,si=e=>e===""||e===!0;function x_(e,t){if(typeof t.tagPriority=="number")return t.tagPriority;let r=100;const n=P_[t.tagPriority]||0,i=e.resolvedOptions.disableCapoSorting?{link:{},script:{},style:{}}:Zc;if(t.tag in Qc)r=Qc[t.tag];else if(t.tag==="meta"){const o=t.props["http-equiv"]==="content-security-policy"?"content-security-policy":t.props.charset?"charset":t.props.name==="viewport"?"viewport":null;o&&(r=Zc.meta[o])}else if(t.tag==="link"&&t.props.rel)r=i.link[t.props.rel];else if(t.tag==="script"){const o=String(t.props.type);si(t.props.async)?r=i.script.async:t.props.src&&!si(t.props.defer)&&!si(t.props.async)&&o!=="module"&&!o.endsWith("json")||t.innerHTML&&!o.endsWith("json")?r=i.script.sync:(si(t.props.defer)&&t.props.src&&!si(t.props.async)||o==="module")&&(r=i.script.defer)}else t.tag==="style"&&(r=t.innerHTML&&O_.test(t.innerHTML)?i.style.imported:i.style.sync);return(r||100)+n}function ef(e,t){const r=typeof t=="function"?t(e):t,n=r.key||String(e.plugins.size+1);e.plugins.get(n)||(e.plugins.set(n,r),e.hooks.addHooks(r.hooks||{}))}function N_(e={}){const t=T_();t.addHooks(e.hooks||{});const r=!e.document,n=new Map,i=new Map,o=new Set,s={_entryCount:1,plugins:i,dirty:!1,resolvedOptions:e,hooks:t,ssr:r,entries:n,headEntries(){return[...n.values()]},use:a=>ef(s,a),push(a,u){const c={...u||{}};delete c.head;const l=c._index??s._entryCount++,f={_i:l,input:a,options:c},d={_poll(h=!1){s.dirty=!0,!h&&o.add(l),t.callHook("entries:updated",s)},dispose(){n.delete(l)&&s.invalidate()},patch(h){(!c.mode||c.mode==="server"&&r||c.mode==="client"&&!r)&&(f.input=h,n.set(l,f),d._poll())}};return d.patch(a),d},async resolveTags(){const a={tagMap:new Map,tags:[],entries:[...s.entries.values()]};for(await t.callHook("entries:resolve",a);o.size;){const d=o.values().next().value;o.delete(d);const h=n.get(d);if(h){const v={tags:R_(h.input,e.propResolvers||[]).map(p=>Object.assign(p,h.options)),entry:h};await t.callHook("entries:normalize",v),h._tags=v.tags.map((p,b)=>(p._w=x_(s,p),p._p=(h._i<<10)+b,p._d=cu(p),p))}}let u=!1;a.entries.flatMap(d=>(d._tags||[]).map(h=>({...h,props:{...h.props}}))).sort(Jc).reduce((d,h)=>{const v=String(h._d||h._p);if(!d.has(v))return d.set(v,h);const p=d.get(v);if((h?.tagDuplicateStrategy||(b_.has(h.tag)?"merge":null)||(h.key&&h.key===p.key?"merge":null))==="merge"){const E={...p.props};Object.entries(h.props).forEach(([m,g])=>E[m]=m==="style"?new Map([...p.props.style||new Map,...g]):m==="class"?new Set([...p.props.class||new Set,...g]):g),d.set(v,{...h,props:E})}else h._p>>10===p._p>>10&&h.tag==="meta"&&hp(v)?(d.set(v,Object.assign([...Array.isArray(p)?p:[p],h],h)),u=!0):(h._w===p._w?h._p>p._p:h?._wef(s,a)),s.hooks.callHook("init",s),e.init?.forEach(a=>a&&s.push(a)),s}const L_=(e,t)=>qe(t)?My(t):t,pp="usehead";function M_(e){return{install(r){r.config.globalProperties.$unhead=e,r.config.globalProperties.$head=e,r.provide(pp,e)}}.install}function D_(){if(ul()){const e=It(pp);if(!e)throw new Error("useHead() was called without provide context, ensure you call it through the setup() function.");return e}throw new Error("useHead() was called without provide context, ensure you call it through the setup() function.")}function eN(e,t={}){const r=t.head||D_();return r.ssr?r.push(e||{},t):U_(r,e,t)}function U_(e,t,r={}){const n=Jt(!1);let i;return zy(()=>{const s=n.value?{}:ds(t,L_);i?i.patch(s):i=e.push(s,r)}),Zn()&&(Qn(()=>{i.dispose()}),cv(()=>{n.value=!0}),lv(()=>{n.value=!1})),i}const F_=(e,t)=>[],H_=e=>sp({},...F_().map(t=>t.data).reverse()),B_=H_;let Do;function j_(){return Do=$fetch(yl(`builds/meta/${to().app.buildId}.json`),{responseType:"json"}),Do.catch(e=>{console.error("[nuxt] Error fetching app manifest.",e)}),Do}function gp(){return Do||j_()}function mp(e){const t=typeof e=="string"?e:e.path;try{return B_(t)}catch(r){return console.error("[nuxt] Error matching route rules.",r),{}}}async function tf(e,t={}){return null}async function q_(e){return null}let Vr=null;async function V_(){if(Vr)return Vr;const e=document.getElementById("__NUXT_DATA__");if(!e)return{};const t=await $_(e.textContent||""),r=e.dataset.src?await q_(e.dataset.src):void 0;return Vr={...t,...r,...window.__NUXT__},Vr.config?.public&&(Vr.config.public=Fr(Vr.config.public)),Vr}async function $_(e){return await d_(e,Ve()._payloadRevivers)}function W_(e,t){Ve()._payloadRevivers[e]=t}const G_=[["NuxtError",e=>on(e)],["EmptyShallowRef",e=>Bn(e==="_"?void 0:e==="0n"?BigInt(0):ls(e))],["EmptyRef",e=>Jt(e==="_"?void 0:e==="0n"?BigInt(0):ls(e))],["ShallowRef",e=>Bn(e)],["ShallowReactive",e=>dr(e)],["Ref",e=>Jt(e)],["Reactive",e=>Fr(e)]],K_=tr({name:"nuxt:revive-payload:client",order:-30,async setup(e){let t,r;for(const[n,i]of G_)W_(n,i);Object.assign(e.payload,([t,r]=Pi(()=>e.runWithContext(V_)),t=await t,r(),t)),delete window.__NUXT__}});async function bl(e,t={}){const r=t.document||e.resolvedOptions.document;if(!r||!e.dirty)return;const n={shouldRender:!0,tags:[]};if(await e.hooks.callHook("dom:beforeRender",n),!!n.shouldRender)return e._domUpdatePromise||(e._domUpdatePromise=new Promise(async i=>{const o=new Map,s=new Promise(h=>{e.resolveTags().then(v=>{h(v.map(p=>{const b=o.get(p._d)||0,E={tag:p,id:(b?`${p._d}:${b}`:p._d)||Yc(p),shouldRender:!0};return p._d&&hp(p._d)&&o.set(p._d,b+1),E}))})});let a=e._dom;if(!a){a={title:r.title,elMap:new Map().set("htmlAttrs",r.documentElement).set("bodyAttrs",r.body)};for(const h of["body","head"]){const v=r[h]?.children;for(const p of v){const b=p.tagName.toLowerCase();if(!Xc.has(b))continue;const E=vp({tag:b,props:{}},{innerHTML:p.innerHTML,...p.getAttributeNames().reduce((m,g)=>(m[g]=p.getAttribute(g),m),{})||{}});if(E.key=p.getAttribute("data-hid")||void 0,E._d=cu(E)||Yc(E),a.elMap.has(E._d)){let m=1,g=E._d;for(;a.elMap.has(g);)g=`${E._d}:${m++}`;a.elMap.set(g,p)}else a.elMap.set(E._d,p)}}}a.pendingSideEffects={...a.sideEffects},a.sideEffects={};function u(h,v,p){const b=`${h}:${v}`;a.sideEffects[b]=p,delete a.pendingSideEffects[b]}function c({id:h,$el:v,tag:p}){const b=p.tag.endsWith("Attrs");a.elMap.set(h,v),b||(p.textContent&&p.textContent!==v.textContent&&(v.textContent=p.textContent),p.innerHTML&&p.innerHTML!==v.innerHTML&&(v.innerHTML=p.innerHTML),u(h,"el",()=>{v?.remove(),a.elMap.delete(h)}));for(const E in p.props){if(!Object.prototype.hasOwnProperty.call(p.props,E))continue;const m=p.props[E];if(E.startsWith("on")&&typeof m=="function"){const y=v?.dataset;if(y&&y[`${E}fired`]){const _=E.slice(0,-5);m.call(v,new Event(_.substring(2)))}v.getAttribute(`data-${E}`)!==""&&((p.tag==="bodyAttrs"?r.defaultView:v).addEventListener(E.substring(2),m.bind(v)),v.setAttribute(`data-${E}`,""));continue}const g=`attr:${E}`;if(E==="class"){if(!m)continue;for(const y of m)b&&u(h,`${g}:${y}`,()=>v.classList.remove(y)),!v.classList.contains(y)&&v.classList.add(y)}else if(E==="style"){if(!m)continue;for(const[y,_]of m)u(h,`${g}:${y}`,()=>{v.style.removeProperty(y)}),v.style.setProperty(y,_)}else m!==!1&&m!==null&&(v.getAttribute(E)!==m&&v.setAttribute(E,m===!0?"":String(m)),b&&u(h,g,()=>v.removeAttribute(E)))}}const l=[],f={bodyClose:void 0,bodyOpen:void 0,head:void 0},d=await s;for(const h of d){const{tag:v,shouldRender:p,id:b}=h;if(p){if(v.tag==="title"){r.title=v.textContent,u("title","",()=>r.title=a.title);continue}h.$el=h.$el||a.elMap.get(b),h.$el?c(h):Xc.has(v.tag)&&l.push(h)}}for(const h of l){const v=h.tag.tagPosition||"head";h.$el=r.createElement(h.tag.tag),c(h),f[v]=f[v]||r.createDocumentFragment(),f[v].appendChild(h.$el)}for(const h of d)await e.hooks.callHook("dom:renderTag",h,r,u);f.head&&r.head.appendChild(f.head),f.bodyOpen&&r.body.insertBefore(f.bodyOpen,r.body.firstChild),f.bodyClose&&r.body.appendChild(f.bodyClose);for(const h in a.pendingSideEffects)a.pendingSideEffects[h]();e._dom=a,await e.hooks.callHook("dom:rendered",{renders:d}),i()}).finally(()=>{e._domUpdatePromise=void 0,e.dirty=!1})),e._domUpdatePromise}function z_(e={}){const t=e.domOptions?.render||bl;e.document=e.document||(typeof window<"u"?document:void 0);const r=e.document?.head.querySelector('script[id="unhead:payload"]')?.innerHTML||!1;return N_({...e,plugins:[...e.plugins||[],{key:"client",hooks:{"entries:updated":t}}],init:[r?JSON.parse(r):!1,...e.init||[]]})}function X_(e,t){let r=0;return()=>{const n=++r;t(()=>{r===n&&e()})}}function Y_(e={}){const t=z_({domOptions:{render:X_(()=>bl(t),r=>setTimeout(r,0))},...e});return t.install=M_(t),t}const J_={disableDefaults:!0},Q_=tr({name:"nuxt:head",enforce:"pre",setup(e){const t=Y_(J_);e.vueApp.use(t);{let r=!0;const n=async()=>{r=!1,await bl(t)};t.hooks.hook("dom:beforeRender",i=>{i.shouldRender=!r}),e.hooks.hook("page:start",()=>{r=!0}),e.hooks.hook("page:finish",()=>{e.isHydrating||n()}),e.hooks.hook("app:error",n),e.hooks.hook("app:suspense:resolve",n)}}});const _n=typeof document<"u";function yp(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Z_(e){return e.__esModule||e[Symbol.toStringTag]==="Module"||e.default&&yp(e.default)}const Ie=Object.assign;function fa(e,t){const r={};for(const n in t){const i=t[n];r[n]=Vt(i)?i.map(e):e(i)}return r}const Oi=()=>{},Vt=Array.isArray;function rf(e,t){const r={};for(const n in e)r[n]=n in t?t[n]:e[n];return r}const bp=/#/g,eS=/&/g,tS=/\//g,rS=/=/g,nS=/\?/g,Ep=/\+/g,iS=/%5B/g,oS=/%5D/g,_p=/%5E/g,sS=/%60/g,Sp=/%7B/g,aS=/%7C/g,wp=/%7D/g,uS=/%20/g;function El(e){return e==null?"":encodeURI(""+e).replace(aS,"|").replace(iS,"[").replace(oS,"]")}function lS(e){return El(e).replace(Sp,"{").replace(wp,"}").replace(_p,"^")}function fu(e){return El(e).replace(Ep,"%2B").replace(uS,"+").replace(bp,"%23").replace(eS,"%26").replace(sS,"`").replace(Sp,"{").replace(wp,"}").replace(_p,"^")}function cS(e){return fu(e).replace(rS,"%3D")}function fS(e){return El(e).replace(bp,"%23").replace(nS,"%3F")}function dS(e){return fS(e).replace(tS,"%2F")}function zi(e){if(e==null)return null;try{return decodeURIComponent(""+e)}catch{}return""+e}const hS=/\/$/,vS=e=>e.replace(hS,"");function da(e,t,r="/"){let n,i={},o="",s="";const a=t.indexOf("#");let u=t.indexOf("?");return u=a>=0&&u>a?-1:u,u>=0&&(n=t.slice(0,u),o=t.slice(u,a>0?a:t.length),i=e(o.slice(1))),a>=0&&(n=n||t.slice(0,a),s=t.slice(a,t.length)),n=yS(n??t,r),{fullPath:n+o+s,path:n,query:i,hash:zi(s)}}function pS(e,t){const r=t.query?e(t.query):"";return t.path+(r&&"?")+r+(t.hash||"")}function nf(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function gS(e,t,r){const n=t.matched.length-1,i=r.matched.length-1;return n>-1&&n===i&&Wn(t.matched[n],r.matched[i])&&Tp(t.params,r.params)&&e(t.query)===e(r.query)&&t.hash===r.hash}function Wn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Tp(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(var r in e)if(!mS(e[r],t[r]))return!1;return!0}function mS(e,t){return Vt(e)?of(e,t):Vt(t)?of(t,e):e?.valueOf()===t?.valueOf()}function of(e,t){return Vt(t)?e.length===t.length&&e.every((r,n)=>r===t[n]):e.length===1&&e[0]===t}function yS(e,t){if(e.startsWith("/"))return e;if(!e)return t;const r=t.split("/"),n=e.split("/"),i=n[n.length-1];(i===".."||i===".")&&n.push("");let o=r.length-1,s,a;for(s=0;s1&&o--;else break;return r.slice(0,o).join("/")+"/"+n.slice(s).join("/")}const Rt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};let du=(function(e){return e.pop="pop",e.push="push",e})({}),ha=(function(e){return e.back="back",e.forward="forward",e.unknown="",e})({});function bS(e){if(!e)if(_n){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),vS(e)}const ES=/^[^#]+#/;function _S(e,t){return e.replace(ES,"#")+t}function SS(e,t){const r=document.documentElement.getBoundingClientRect(),n=e.getBoundingClientRect();return{behavior:t.behavior,left:n.left-r.left-(t.left||0),top:n.top-r.top-(t.top||0)}}const Ms=()=>({left:window.scrollX,top:window.scrollY});function wS(e){let t;if("el"in e){const r=e.el,n=typeof r=="string"&&r.startsWith("#"),i=typeof r=="string"?n?document.getElementById(r.slice(1)):document.querySelector(r):r;if(!i)return;t=SS(i,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.scrollX,t.top!=null?t.top:window.scrollY)}function sf(e,t){return(history.state?history.state.position-t:-1)+e}const hu=new Map;function TS(e,t){hu.set(e,t)}function AS(e){const t=hu.get(e);return hu.delete(e),t}function IS(e){return typeof e=="string"||e&&typeof e=="object"}function Ap(e){return typeof e=="string"||typeof e=="symbol"}let Be=(function(e){return e[e.MATCHER_NOT_FOUND=1]="MATCHER_NOT_FOUND",e[e.NAVIGATION_GUARD_REDIRECT=2]="NAVIGATION_GUARD_REDIRECT",e[e.NAVIGATION_ABORTED=4]="NAVIGATION_ABORTED",e[e.NAVIGATION_CANCELLED=8]="NAVIGATION_CANCELLED",e[e.NAVIGATION_DUPLICATED=16]="NAVIGATION_DUPLICATED",e})({});const Ip=Symbol("");Be.MATCHER_NOT_FOUND+"",Be.NAVIGATION_GUARD_REDIRECT+"",Be.NAVIGATION_ABORTED+"",Be.NAVIGATION_CANCELLED+"",Be.NAVIGATION_DUPLICATED+"";function Gn(e,t){return Ie(new Error,{type:e,[Ip]:!0},t)}function ir(e,t){return e instanceof Error&&Ip in e&&(t==null||!!(e.type&t))}const CS=["params","query","hash"];function kS(e){if(typeof e=="string")return e;if(e.path!=null)return e.path;const t={};for(const r of CS)r in e&&(t[r]=e[r]);return JSON.stringify(t,null,2)}function RS(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let n=0;ni&&fu(i)):[n&&fu(n)]).forEach(i=>{i!==void 0&&(t+=(t.length?"&":"")+r,i!=null&&(t+="="+i))})}return t}function PS(e){const t={};for(const r in e){const n=e[r];n!==void 0&&(t[r]=Vt(n)?n.map(i=>i==null?null:""+i):n==null?n:""+n)}return t}const OS=Symbol(""),uf=Symbol(""),_l=Symbol(""),Cp=Symbol(""),vu=Symbol("");function ai(){let e=[];function t(n){return e.push(n),()=>{const i=e.indexOf(n);i>-1&&e.splice(i,1)}}function r(){e=[]}return{add:t,list:()=>e.slice(),reset:r}}function Ir(e,t,r,n,i,o=s=>s()){const s=n&&(n.enterCallbacks[i]=n.enterCallbacks[i]||[]);return()=>new Promise((a,u)=>{const c=d=>{d===!1?u(Gn(Be.NAVIGATION_ABORTED,{from:r,to:t})):d instanceof Error?u(d):IS(d)?u(Gn(Be.NAVIGATION_GUARD_REDIRECT,{from:t,to:d})):(s&&n.enterCallbacks[i]===s&&typeof d=="function"&&s.push(d),a())},l=o(()=>e.call(n&&n.instances[i],t,r,c));let f=Promise.resolve(l);e.length<3&&(f=f.then(c)),f.catch(d=>u(d))})}function va(e,t,r,n,i=o=>o()){const o=[];for(const s of e)for(const a in s.components){let u=s.components[a];if(!(t!=="beforeRouteEnter"&&!s.instances[a]))if(yp(u)){const c=(u.__vccOpts||u)[t];c&&o.push(Ir(c,r,n,s,a,i))}else{let c=u();o.push(()=>c.then(l=>{if(!l)throw new Error(`Couldn't resolve component "${a}" at "${s.path}"`);const f=Z_(l)?l.default:l;s.mods[a]=l,s.components[a]=f;const d=(f.__vccOpts||f)[t];return d&&Ir(d,r,n,s,a,i)()}))}}return o}function xS(e,t){const r=[],n=[],i=[],o=Math.max(t.matched.length,e.matched.length);for(let s=0;sWn(c,a))?n.push(a):r.push(a));const u=e.matched[s];u&&(t.matched.find(c=>Wn(c,u))||i.push(u))}return[r,n,i]}let NS=()=>location.protocol+"//"+location.host;function kp(e,t){const{pathname:r,search:n,hash:i}=t,o=e.indexOf("#");if(o>-1){let s=i.includes(e.slice(o))?e.slice(o).length:1,a=i.slice(s);return a[0]!=="/"&&(a="/"+a),nf(a,"")}return nf(r,e)+n+i}function LS(e,t,r,n){let i=[],o=[],s=null;const a=({state:d})=>{const h=kp(e,location),v=r.value,p=t.value;let b=0;if(d){if(r.value=h,t.value=d,s&&s===v){s=null;return}b=p?d.position-p.position:0}else n(h);i.forEach(E=>{E(r.value,v,{delta:b,type:du.pop,direction:b?b>0?ha.forward:ha.back:ha.unknown})})};function u(){s=r.value}function c(d){i.push(d);const h=()=>{const v=i.indexOf(d);v>-1&&i.splice(v,1)};return o.push(h),h}function l(){if(document.visibilityState==="hidden"){const{history:d}=window;if(!d.state)return;d.replaceState(Ie({},d.state,{scroll:Ms()}),"")}}function f(){for(const d of o)d();o=[],window.removeEventListener("popstate",a),window.removeEventListener("pagehide",l),document.removeEventListener("visibilitychange",l)}return window.addEventListener("popstate",a),window.addEventListener("pagehide",l),document.addEventListener("visibilitychange",l),{pauseListeners:u,listen:c,destroy:f}}function lf(e,t,r,n=!1,i=!1){return{back:e,current:t,forward:r,replaced:n,position:window.history.length,scroll:i?Ms():null}}function MS(e){const{history:t,location:r}=window,n={value:kp(e,r)},i={value:t.state};i.value||o(n.value,{back:null,current:n.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function o(u,c,l){const f=e.indexOf("#"),d=f>-1?(r.host&&document.querySelector("base")?e:e.slice(f))+u:NS()+e+u;try{t[l?"replaceState":"pushState"](c,"",d),i.value=c}catch(h){console.error(h),r[l?"replace":"assign"](d)}}function s(u,c){o(u,Ie({},t.state,lf(i.value.back,u,i.value.forward,!0),c,{position:i.value.position}),!0),n.value=u}function a(u,c){const l=Ie({},i.value,t.state,{forward:u,scroll:Ms()});o(l.current,l,!0),o(u,Ie({},lf(n.value,u,null),{position:l.position+1},c),!1),n.value=u}return{location:n,state:i,push:a,replace:s}}function DS(e){e=bS(e);const t=MS(e),r=LS(e,t.state,t.location,t.replace);function n(o,s=!0){s||r.pauseListeners(),history.go(o)}const i=Ie({location:"",base:e,go:n,createHref:_S.bind(null,e)},t,r);return Object.defineProperty(i,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(i,"state",{enumerable:!0,get:()=>t.state.value}),i}let Jr=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.Group=2]="Group",e})({});var Ke=(function(e){return e[e.Static=0]="Static",e[e.Param=1]="Param",e[e.ParamRegExp=2]="ParamRegExp",e[e.ParamRegExpEnd=3]="ParamRegExpEnd",e[e.EscapeNext=4]="EscapeNext",e})(Ke||{});const US={type:Jr.Static,value:""},FS=/[a-zA-Z0-9_]/;function HS(e){if(!e)return[[]];if(e==="/")return[[US]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${r})/"${c}": ${h}`)}let r=Ke.Static,n=r;const i=[];let o;function s(){o&&i.push(o),o=[]}let a=0,u,c="",l="";function f(){c&&(r===Ke.Static?o.push({type:Jr.Static,value:c}):r===Ke.Param||r===Ke.ParamRegExp||r===Ke.ParamRegExpEnd?(o.length>1&&(u==="*"||u==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),o.push({type:Jr.Param,value:c,regexp:l,repeatable:u==="*"||u==="+",optional:u==="*"||u==="?"})):t("Invalid state to consume buffer"),c="")}function d(){c+=u}for(;at.length?t.length===1&&t[0]===ct.Static+ct.Segment?1:-1:0}function Rp(e,t){let r=0;const n=e.score,i=t.score;for(;r0&&t[t.length-1]<0}const $S={strict:!1,end:!0,sensitive:!1};function WS(e,t,r){const n=qS(HS(e.path),r),i=Ie(n,{record:e,parent:t,children:[],alias:[]});return t&&!i.record.aliasOf==!t.record.aliasOf&&t.children.push(i),i}function GS(e,t){const r=[],n=new Map;t=rf($S,t);function i(f){return n.get(f)}function o(f,d,h){const v=!h,p=hf(f);p.aliasOf=h&&h.record;const b=rf(t,f),E=[p];if("alias"in f){const y=typeof f.alias=="string"?[f.alias]:f.alias;for(const _ of y)E.push(hf(Ie({},p,{components:h?h.record.components:p.components,path:_,aliasOf:h?h.record:p})))}let m,g;for(const y of E){const{path:_}=y;if(d&&_[0]!=="/"){const S=d.record.path,A=S[S.length-1]==="/"?"":"/";y.path=d.record.path+(_&&A+_)}if(m=WS(y,d,b),h?h.alias.push(m):(g=g||m,g!==m&&g.alias.push(m),v&&f.name&&!vf(m)&&s(f.name)),Pp(m)&&u(m),p.children){const S=p.children;for(let A=0;A{s(g)}:Oi}function s(f){if(Ap(f)){const d=n.get(f);d&&(n.delete(f),r.splice(r.indexOf(d),1),d.children.forEach(s),d.alias.forEach(s))}else{const d=r.indexOf(f);d>-1&&(r.splice(d,1),f.record.name&&n.delete(f.record.name),f.children.forEach(s),f.alias.forEach(s))}}function a(){return r}function u(f){const d=XS(f,r);r.splice(d,0,f),f.record.name&&!vf(f)&&n.set(f.record.name,f)}function c(f,d){let h,v={},p,b;if("name"in f&&f.name){if(h=n.get(f.name),!h)throw Gn(Be.MATCHER_NOT_FOUND,{location:f});b=h.record.name,v=Ie(df(d.params,h.keys.filter(g=>!g.optional).concat(h.parent?h.parent.keys.filter(g=>g.optional):[]).map(g=>g.name)),f.params&&df(f.params,h.keys.map(g=>g.name))),p=h.stringify(v)}else if(f.path!=null)p=f.path,h=r.find(g=>g.re.test(p)),h&&(v=h.parse(p),b=h.record.name);else{if(h=d.name?n.get(d.name):r.find(g=>g.re.test(d.path)),!h)throw Gn(Be.MATCHER_NOT_FOUND,{location:f,currentLocation:d});b=h.record.name,v=Ie({},d.params,f.params),p=h.stringify(v)}const E=[];let m=h;for(;m;)E.unshift(m.record),m=m.parent;return{name:b,path:p,params:v,matched:E,meta:zS(E)}}e.forEach(f=>o(f));function l(){r.length=0,n.clear()}return{addRoute:o,resolve:c,removeRoute:s,clearRoutes:l,getRoutes:a,getRecordMatcher:i}}function df(e,t){const r={};for(const n of t)n in e&&(r[n]=e[n]);return r}function hf(e){const t={path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:e.aliasOf,beforeEnter:e.beforeEnter,props:KS(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}};return Object.defineProperty(t,"mods",{value:{}}),t}function KS(e){const t={},r=e.props||!1;if("component"in e)t.default=r;else for(const n in e.components)t[n]=typeof r=="object"?r[n]:r;return t}function vf(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function zS(e){return e.reduce((t,r)=>Ie(t,r.meta),{})}function XS(e,t){let r=0,n=t.length;for(;r!==n;){const o=r+n>>1;Rp(e,t[o])<0?n=o:r=o+1}const i=YS(e);return i&&(n=t.lastIndexOf(i,n-1)),n}function YS(e){let t=e;for(;t=t.parent;)if(Pp(t)&&Rp(e,t)===0)return t}function Pp({record:e}){return!!(e.name||e.components&&Object.keys(e.components).length||e.redirect)}function pf(e){const t=It(_l),r=It(Cp),n=We(()=>{const u=ke(e.to);return t.resolve(u)}),i=We(()=>{const{matched:u}=n.value,{length:c}=u,l=u[c-1],f=r.matched;if(!l||!f.length)return-1;const d=f.findIndex(Wn.bind(null,l));if(d>-1)return d;const h=gf(u[c-2]);return c>1&&gf(l)===h&&f[f.length-1].path!==h?f.findIndex(Wn.bind(null,u[c-2])):d}),o=We(()=>i.value>-1&&tw(r.params,n.value.params)),s=We(()=>i.value>-1&&i.value===r.matched.length-1&&Tp(r.params,n.value.params));function a(u={}){if(ew(u)){const c=t[ke(e.replace)?"replace":"push"](ke(e.to)).catch(Oi);return e.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>c),c}return Promise.resolve()}return{route:n,href:We(()=>n.value.href),isActive:o,isExactActive:s,navigate:a}}function JS(e){return e.length===1?e[0]:e}const QS=fn({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:pf,setup(e,{slots:t}){const r=Fr(pf(e)),{options:n}=It(_l),i=We(()=>({[mf(e.activeClass,n.linkActiveClass,"router-link-active")]:r.isActive,[mf(e.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:r.isExactActive}));return()=>{const o=t.default&&JS(t.default(r));return e.custom?o:Et("a",{"aria-current":r.isExactActive?e.ariaCurrentValue:null,href:r.href,onClick:r.navigate,class:i.value},o)}}}),ZS=QS;function ew(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function tw(e,t){for(const r in t){const n=t[r],i=e[r];if(typeof n=="string"){if(n!==i)return!1}else if(!Vt(i)||i.length!==n.length||n.some((o,s)=>o.valueOf()!==i[s].valueOf()))return!1}return!0}function gf(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const mf=(e,t,r)=>e??t??r,rw=fn({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:r}){const n=It(vu),i=We(()=>e.route||n.value),o=It(uf,0),s=We(()=>{let c=ke(o);const{matched:l}=i.value;let f;for(;(f=l[c])&&!f.components;)c++;return c}),a=We(()=>i.value.matched[s.value]);On(uf,We(()=>s.value+1)),On(OS,a),On(vu,i);const u=Jt();return xn(()=>[u.value,a.value,e.name],([c,l,f],[d,h,v])=>{l&&(l.instances[f]=c,h&&h!==l&&c&&c===d&&(l.leaveGuards.size||(l.leaveGuards=h.leaveGuards),l.updateGuards.size||(l.updateGuards=h.updateGuards))),c&&l&&(!h||!Wn(l,h)||!d)&&(l.enterCallbacks[f]||[]).forEach(p=>p(c))},{flush:"post"}),()=>{const c=i.value,l=e.name,f=a.value,d=f&&f.components[l];if(!d)return yf(r.default,{Component:d,route:c});const h=f.props[l],v=h?h===!0?c.params:typeof h=="function"?h(c):h:null,b=Et(d,Ie({},v,t,{onVnodeUnmounted:E=>{E.component.isUnmounted&&(f.instances[l]=null)},ref:u}));return yf(r.default,{Component:b,route:c})||b}}});function yf(e,t){if(!e)return null;const r=e(t);return r.length===1?r[0]:r}const Op=rw;function nw(e){const t=GS(e.routes,e),r=e.parseQuery||RS,n=e.stringifyQuery||af,i=e.history,o=ai(),s=ai(),a=ai(),u=Bn(Rt);let c=Rt;_n&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const l=fa.bind(null,M=>""+M),f=fa.bind(null,dS),d=fa.bind(null,zi);function h(M,Z){let X,re;return Ap(M)?(X=t.getRecordMatcher(M),re=Z):re=M,t.addRoute(re,X)}function v(M){const Z=t.getRecordMatcher(M);Z&&t.removeRoute(Z)}function p(){return t.getRoutes().map(M=>M.record)}function b(M){return!!t.getRecordMatcher(M)}function E(M,Z){if(Z=Ie({},Z||u.value),typeof M=="string"){const T=da(r,M,Z.path),L=t.resolve({path:T.path},Z),H=i.createHref(T.fullPath);return Ie(T,L,{params:d(L.params),hash:zi(T.hash),redirectedFrom:void 0,href:H})}let X;if(M.path!=null)X=Ie({},M,{path:da(r,M.path,Z.path).path});else{const T=Ie({},M.params);for(const L in T)T[L]==null&&delete T[L];X=Ie({},M,{params:f(T)}),Z.params=f(Z.params)}const re=t.resolve(X,Z),F=M.hash||"";re.params=l(d(re.params));const ge=pS(n,Ie({},M,{hash:lS(F),path:re.path})),w=i.createHref(ge);return Ie({fullPath:ge,hash:F,query:n===af?PS(M.query):M.query||{}},re,{redirectedFrom:void 0,href:w})}function m(M){return typeof M=="string"?da(r,M,u.value.path):Ie({},M)}function g(M,Z){if(c!==M)return Gn(Be.NAVIGATION_CANCELLED,{from:Z,to:M})}function y(M){return A(M)}function _(M){return y(Ie(m(M),{replace:!0}))}function S(M,Z){const X=M.matched[M.matched.length-1];if(X&&X.redirect){const{redirect:re}=X;let F=typeof re=="function"?re(M,Z):re;return typeof F=="string"&&(F=F.includes("?")||F.includes("#")?F=m(F):{path:F},F.params={}),Ie({query:M.query,hash:M.hash,params:F.path!=null?{}:M.params},F)}}function A(M,Z){const X=c=E(M),re=u.value,F=M.state,ge=M.force,w=M.replace===!0,T=S(X,re);if(T)return A(Ie(m(T),{state:typeof T=="object"?Ie({},F,T.state):F,force:ge,replace:w}),Z||X);const L=X;L.redirectedFrom=Z;let H;return!ge&&gS(n,re,X)&&(H=Gn(Be.NAVIGATION_DUPLICATED,{to:L,from:re}),fe(re,re,!0,!1)),(H?Promise.resolve(H):R(L,re)).catch(U=>ir(U)?ir(U,Be.NAVIGATION_GUARD_REDIRECT)?U:ae(U):K(U,L,re)).then(U=>{if(U){if(ir(U,Be.NAVIGATION_GUARD_REDIRECT))return A(Ie({replace:w},m(U.to),{state:typeof U.to=="object"?Ie({},F,U.to.state):F,force:ge}),Z||L)}else U=k(L,re,!0,w,F);return P(L,re,U),U})}function I(M,Z){const X=g(M,Z);return X?Promise.reject(X):Promise.resolve()}function C(M){const Z=be.values().next().value;return Z&&typeof Z.runWithContext=="function"?Z.runWithContext(M):M()}function R(M,Z){let X;const[re,F,ge]=xS(M,Z);X=va(re.reverse(),"beforeRouteLeave",M,Z);for(const T of re)T.leaveGuards.forEach(L=>{X.push(Ir(L,M,Z))});const w=I.bind(null,M,Z);return X.push(w),me(X).then(()=>{X=[];for(const T of o.list())X.push(Ir(T,M,Z));return X.push(w),me(X)}).then(()=>{X=va(F,"beforeRouteUpdate",M,Z);for(const T of F)T.updateGuards.forEach(L=>{X.push(Ir(L,M,Z))});return X.push(w),me(X)}).then(()=>{X=[];for(const T of ge)if(T.beforeEnter)if(Vt(T.beforeEnter))for(const L of T.beforeEnter)X.push(Ir(L,M,Z));else X.push(Ir(T.beforeEnter,M,Z));return X.push(w),me(X)}).then(()=>(M.matched.forEach(T=>T.enterCallbacks={}),X=va(ge,"beforeRouteEnter",M,Z,C),X.push(w),me(X))).then(()=>{X=[];for(const T of s.list())X.push(Ir(T,M,Z));return X.push(w),me(X)}).catch(T=>ir(T,Be.NAVIGATION_CANCELLED)?T:Promise.reject(T))}function P(M,Z,X){a.list().forEach(re=>C(()=>re(M,Z,X)))}function k(M,Z,X,re,F){const ge=g(M,Z);if(ge)return ge;const w=Z===Rt,T=_n?history.state:{};X&&(re||w?i.replace(M.fullPath,Ie({scroll:w&&T&&T.scroll},F)):i.push(M.fullPath,F)),u.value=M,fe(M,Z,X,w),ae()}let N;function $(){N||(N=i.listen((M,Z,X)=>{if(!de.listening)return;const re=E(M),F=S(re,de.currentRoute.value);if(F){A(Ie(F,{replace:!0,force:!0}),re).catch(Oi);return}c=re;const ge=u.value;_n&&TS(sf(ge.fullPath,X.delta),Ms()),R(re,ge).catch(w=>ir(w,Be.NAVIGATION_ABORTED|Be.NAVIGATION_CANCELLED)?w:ir(w,Be.NAVIGATION_GUARD_REDIRECT)?(A(Ie(m(w.to),{force:!0}),re).then(T=>{ir(T,Be.NAVIGATION_ABORTED|Be.NAVIGATION_DUPLICATED)&&!X.delta&&X.type===du.pop&&i.go(-1,!1)}).catch(Oi),Promise.reject()):(X.delta&&i.go(-X.delta,!1),K(w,re,ge))).then(w=>{w=w||k(re,ge,!1),w&&(X.delta&&!ir(w,Be.NAVIGATION_CANCELLED)?i.go(-X.delta,!1):X.type===du.pop&&ir(w,Be.NAVIGATION_ABORTED|Be.NAVIGATION_DUPLICATED)&&i.go(-1,!1)),P(re,ge,w)}).catch(Oi)}))}let ee=ai(),B=ai(),z;function K(M,Z,X){ae(M);const re=B.list();return re.length?re.forEach(F=>F(M,Z,X)):console.error(M),Promise.reject(M)}function Q(){return z&&u.value!==Rt?Promise.resolve():new Promise((M,Z)=>{ee.add([M,Z])})}function ae(M){return z||(z=!M,$(),ee.list().forEach(([Z,X])=>M?X(M):Z()),ee.reset()),M}function fe(M,Z,X,re){const{scrollBehavior:F}=e;if(!_n||!F)return Promise.resolve();const ge=!X&&AS(sf(M.fullPath,0))||(re||!X)&&history.state&&history.state.scroll||null;return qi().then(()=>F(M,Z,ge)).then(w=>w&&wS(w)).catch(w=>K(w,M,Z))}const pe=M=>i.go(M);let ye;const be=new Set,de={currentRoute:u,listening:!0,addRoute:h,removeRoute:v,clearRoutes:t.clearRoutes,hasRoute:b,getRoutes:p,resolve:E,options:e,push:y,replace:_,go:pe,back:()=>pe(-1),forward:()=>pe(1),beforeEach:o.add,beforeResolve:s.add,afterEach:a.add,onError:B.add,isReady:Q,install(M){M.component("RouterLink",ZS),M.component("RouterView",Op),M.config.globalProperties.$router=de,Object.defineProperty(M.config.globalProperties,"$route",{enumerable:!0,get:()=>ke(u)}),_n&&!ye&&u.value===Rt&&(ye=!0,y(i.location).catch(re=>{}));const Z={};for(const re in Rt)Object.defineProperty(Z,re,{get:()=>u.value[re],enumerable:!0});M.provide(_l,de),M.provide(Cp,dr(Z)),M.provide(vu,u);const X=M.unmount;be.add(M),M.unmount=function(){be.delete(M),be.size<1&&(c=Rt,N&&N(),N=null,u.value=Rt,ye=!1,z=!1),X()}}};function me(M){return M.reduce((Z,X)=>Z.then(()=>C(X)),Promise.resolve())}return de}const iw=/(:\w+)\([^)]+\)/g,ow=/(:\w+)[?+*]/g,sw=/:\w+/g,aw=(e,t)=>t.path.replace(iw,"$1").replace(ow,"$1").replace(sw,r=>e.params[r.slice(1)]?.toString()||""),pu=(e,t)=>{const r=e.route.matched.find(i=>i.components?.default===e.Component.type),n=t??r?.meta.key??(r&&aw(e.route,r));return typeof n=="function"?n(e.route):n},uw=(e,t)=>({default:()=>e?Et(s0,e===!0?{}:e,t):t});function Sl(e){return Array.isArray(e)?e:[e]}const lw="modulepreload",cw=function(e,t){return new URL(e,t).href},bf={},Bt=function(t,r,n){let i=Promise.resolve();if(r&&r.length>0){let c=function(l){return Promise.all(l.map(f=>Promise.resolve(f).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const s=document.getElementsByTagName("link"),a=document.querySelector("meta[property=csp-nonce]"),u=a?.nonce||a?.getAttribute("nonce");i=c(r.map(l=>{if(l=cw(l,n),l in bf)return;bf[l]=!0;const f=l.endsWith(".css"),d=f?'[rel="stylesheet"]':"";if(n)for(let v=s.length-1;v>=0;v--){const p=s[v];if(p.href===l&&(!f||p.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${l}"]${d}`))return;const h=document.createElement("link");if(h.rel=f?"stylesheet":lw,f||(h.as="script"),h.crossOrigin="",h.href=l,u&&h.setAttribute("nonce",u),document.head.appendChild(h),f)return new Promise((v,p)=>{h.addEventListener("load",v),h.addEventListener("error",()=>p(new Error(`Unable to preload CSS for ${l}`)))})}))}function o(s){const a=new Event("vite:preloadError",{cancelable:!0});if(a.payload=s,window.dispatchEvent(a),!a.defaultPrevented)throw s}return i.then(s=>{for(const a of s||[])a.status==="rejected"&&o(a.reason);return t().catch(o)})},pa=[{name:"index",path:"/",component:()=>Bt(()=>import("./BTkul5Q2.js"),[],import.meta.url)},{name:"burrito",path:"/burrito",meta:{middleware:"auth"},component:()=>Bt(()=>import("./DOp-_5WN.js"),[],import.meta.url)},{name:"profile",path:"/profile",meta:{middleware:"auth"},component:()=>Bt(()=>import("./DkWg1RwU.js"),[],import.meta.url)}],fw=(e,t)=>({default:()=>e?Et(sb,e===!0?{}:e,t):t.default?.()}),dw=/(:\w+)\([^)]+\)/g,hw=/(:\w+)[?+*]/g,vw=/:\w+/g;function Ef(e){const t=e?.meta.key??e.path.replace(dw,"$1").replace(hw,"$1").replace(vw,r=>e.params[r.slice(1)]?.toString()||"");return typeof t=="function"?t(e):t}function pw(e,t){return e===t||t===Rt?!1:Ef(e)!==Ef(t)?!0:!e.matched.every((n,i)=>n.components&&n.components.default===t.matched[i]?.components?.default)}const gw={scrollBehavior(e,t,r){const n=Ve(),i=St().options?.scrollBehaviorType??"auto";if(e.path.replace(/\/$/,"")===t.path.replace(/\/$/,""))return t.hash&&!e.hash?{left:0,top:0}:e.hash?{el:e.hash,top:xp(e.hash),behavior:i}:!1;if((typeof e.meta.scrollToTop=="function"?e.meta.scrollToTop(e,t):e.meta.scrollToTop)===!1)return!1;const s=n._runningTransition?"page:transition:finish":"page:loading:end";return new Promise(a=>{if(t===Rt){a(_f(e,t,r,i));return}n.hooks.hookOnce(s,()=>{requestAnimationFrame(()=>a(_f(e,t,r,i)))})})}};function xp(e){try{const t=document.querySelector(e);if(t)return(Number.parseFloat(getComputedStyle(t).scrollMarginTop)||0)+(Number.parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop)||0)}catch{}return 0}function _f(e,t,r,n){if(r)return r;const i=pw(e,t);return e.hash?{el:e.hash,top:xp(e.hash),behavior:i?n:"instant"}:{left:0,top:0}}const mw={hashMode:!1,scrollBehaviorType:"auto"},_r={...mw,...gw},yw=async(e,t)=>{let r,n;if(!e.meta?.validate)return;const i=([r,n]=Pi(()=>Promise.resolve(e.meta.validate(e))),r=await r,n(),r);if(i===!0)return;const o=on({fatal:!0,status:i&&(i.status||i.statusCode)||404,statusText:i&&(i.statusText||i.statusMessage)||`Page Not Found: ${e.fullPath}`,data:{path:e.fullPath}});return typeof window<"u"&&window.history.pushState({},"",t.fullPath),o},bw=e=>{const t=mp({path:e.path});if(t.redirect){const r=t.redirect.includes("#")?t.redirect:t.redirect+e.hash;return Hr(r,{acceptRelative:!0})?(window.location.href=r,!1):r}},Ew=[yw,bw],gu={auth:()=>Bt(()=>import("./iMBneS3o.js"),[],import.meta.url)};function _w(e,t,r){const{pathname:n,search:i,hash:o}=t,s=e.indexOf("#");if(s>-1){const c=o.includes(e.slice(s))?e.slice(s).length:1;let l=o.slice(c);return l[0]!=="/"&&(l="/"+l),Fc(Hc(l,""))}const a=Fc(Hc(n,e)),u=!r||aE(a,r)?a:r;return u+(u.includes("?")?"":i)+o}const Sw=tr({name:"nuxt:router",enforce:"pre",async setup(e){let t,r,n=to().app.baseURL;const i=_r.history?.(n)??DS(n),o=_r.routes?([t,r]=Pi(()=>_r.routes(pa)),t=await t,r(),t??pa):pa;let s;const a=nw({..._r,scrollBehavior:(b,E,m)=>{if(E===Rt){s=m;return}if(_r.scrollBehavior){if(a.options.scrollBehavior=_r.scrollBehavior,"scrollRestoration"in window.history){const g=a.beforeEach(()=>{g(),window.history.scrollRestoration="manual"})}return _r.scrollBehavior(b,Rt,s||m)}},history:i,routes:o});"scrollRestoration"in window.history&&(window.history.scrollRestoration="auto"),e.vueApp.use(a);const u=Bn(a.currentRoute.value);a.afterEach((b,E)=>{u.value=E}),Object.defineProperty(e.vueApp.config.globalProperties,"previousRoute",{get:()=>u.value});const c=_w(n,window.location,e.payload.path),l=Bn(a.currentRoute.value),f=()=>{l.value=a.currentRoute.value};a.afterEach((b,E)=>{b.matched.at(-1)?.components?.default===E.matched.at(-1)?.components?.default&&f()});const d={sync:f};for(const b in l.value)Object.defineProperty(d,b,{get:()=>l.value[b],enumerable:!0});e._route=dr(d),e._middleware||={global:[],named:{}};const h=Ls();a.afterEach(async(b,E,m)=>{delete e._processingMiddleware,!e.isHydrating&&h.value&&await e.runWithContext(r_),m&&await e.callHook("page:loading:end")});try{[t,r]=Pi(()=>a.isReady()),await t,r()}catch(b){[t,r]=Pi(()=>e.runWithContext(()=>Kr(b))),await t,r()}const v=c!==a.currentRoute.value.fullPath?a.resolve(c):a.currentRoute.value;f();const p=e.payload.state._layout;return a.beforeEach(async(b,E)=>{await e.callHook("page:loading:start"),b.meta=Fr(b.meta),e.isHydrating&&p&&!gr(b.meta.layout)&&(b.meta.layout=p),e._processingMiddleware=!0;{const m=new Set([...Ew,...e._middleware.global]);for(const y of b.matched){const _=y.meta.middleware;if(_)for(const S of Sl(_))m.add(S)}const g=mp({path:b.path});if(g.appMiddleware)for(const y in g.appMiddleware)g.appMiddleware[y]?m.add(y):m.delete(y);for(const y of m){const _=typeof y=="string"?e._middleware.named[y]||await gu[y]?.().then(S=>S.default||S):y;if(!_)throw new Error(`Unknown route middleware: '${y}'.`);try{const S=await e.runWithContext(()=>_(b,E));if(!e.payload.serverRendered&&e.isHydrating&&(S===!1||S instanceof Error)){const A=S||on({status:404,statusText:`Page Not Found: ${c}`});return await e.runWithContext(()=>Kr(A)),!1}if(S===!0)continue;if(S===!1)return S;if(S)return cp(S)&&S.fatal&&await e.runWithContext(()=>Kr(S)),S}catch(S){const A=on(S);return A.fatal&&await e.runWithContext(()=>Kr(A)),A}}}}),a.onError(async()=>{delete e._processingMiddleware,await e.callHook("page:loading:end")}),a.afterEach(b=>{if(b.matched.length===0)return e.runWithContext(()=>Kr(on({status:404,fatal:!1,statusText:`Page not found: ${b.fullPath}`,data:{path:b.fullPath}})))}),e.hooks.hookOnce("app:created",async()=>{try{"name"in v&&(v.name=void 0),await a.replace({...v,force:!0}),a.options.scrollBehavior=_r.scrollBehavior}catch(b){await e.runWithContext(()=>Kr(b))}}),{provide:{router:a}}}}),mu=globalThis.requestIdleCallback||(e=>{const t=Date.now(),r={didTimeout:!1,timeRemaining:()=>Math.max(0,50-(Date.now()-t))};return setTimeout(()=>{e(r)},1)}),ww=globalThis.cancelIdleCallback||(e=>{clearTimeout(e)}),Ds=e=>{const t=Ve();t.isHydrating?t.hooks.hookOnce("app:suspense:resolve",()=>{mu(()=>e())}):mu(()=>e())},Tw=tr({name:"nuxt:payload",setup(e){const t=new Set;St().beforeResolve(async(r,n)=>{if(r.path===n.path)return;const i=await tf(r.path);if(i){for(const o of t)delete e.static.data[o];for(const o in i.data)o in e.static.data||t.add(o),e.static.data[o]=i.data[o]}}),Ds(()=>{e.hooks.hook("link:prefetch",async r=>{const{hostname:n}=new URL(r,window.location.href);n===window.location.hostname&&await tf().catch(()=>{console.warn("[nuxt] Error preloading payload for",r)})}),navigator.connection?.effectiveType!=="slow-2g"&&setTimeout(gp,1e3)})}}),Aw=tr(()=>{const e=St();Ds(()=>{e.beforeResolve(async()=>{await new Promise(t=>{setTimeout(t,100),requestAnimationFrame(()=>{setTimeout(t,0)})})})})}),Iw=tr(e=>{let t;async function r(){let n;try{n=await gp()}catch(i){const o=i;if(!("status"in o&&(o.status===404||o.status===403)))throw o}t&&clearTimeout(t),t=setTimeout(r,Gc);try{const i=await $fetch(yl("builds/latest.json")+`?${Date.now()}`);i.id!==n?.id&&(e.hooks.callHook("app:manifest:update",i),t&&clearTimeout(t))}catch{}}Ds(()=>{t=setTimeout(r,Gc)})});function Cw(e={}){const t=e.path||window.location.pathname;let r={};try{r=ls(sessionStorage.getItem("nuxt:reload")||"{}")}catch{}if(e.force||r?.path!==t||r?.expires{n.clear()}),e.hook("app:chunkError",({error:o})=>{n.add(o)});function i(o){const s=gl(r.app.baseURL,o.fullPath);Cw({path:s,persistState:!0})}e.hook("app:manifest:update",()=>{t.beforeResolve(i)}),t.onError((o,s)=>{n.has(o)&&i(s)})}}),Rw=tr({name:"nuxt:global-components"}),mo={};function Pw(e){if(e?.__asyncLoader&&!e.__asyncResolved)return e.__asyncLoader()}async function Np(e,t=St()){const{path:r,matched:n}=t.resolve(e);if(!n.length||(t._routePreloaded||=new Set,t._routePreloaded.has(r)))return;const i=t._preloadPromises||=[];if(i.length>4)return Promise.all(i).then(()=>Np(e,t));t._routePreloaded.add(r);for(const o of n){const s=o.components?.default;if(typeof s!="function")continue;const a=Promise.resolve(s()).catch(()=>{}).finally(()=>i.splice(i.indexOf(a)));i.push(a)}await Promise.all(i)}const Ow=tr({name:"nuxt:prefetch",setup(e){const t=St();e.hooks.hook("app:mounted",()=>{t.beforeEach(async r=>{const n=r?.meta?.layout;n&&typeof mo[n]=="function"&&await mo[n]()})}),e.hooks.hook("link:prefetch",r=>{if(Hr(r))return;const n=t.resolve(r);if(!n)return;const i=n.meta.layout;let o=Sl(n.meta.middleware);o=o.filter(s=>typeof s=="string");for(const s of o)typeof gu[s]=="function"&&gu[s]();typeof i=="string"&&i in mo&&Pw(mo[i])})}});var yu=function(e,t){return yu=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},yu(e,t)};function Mt(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");yu(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var D=function(){return D=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function oe(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function Ee(e,t,r){if(r||arguments.length===2)for(var n=0,i=t.length,o;nBw)return!1;for(var t in e){var r=e[t];if(!Fp(t,r))return!1}return!0},Fp=function(e,t){var r,n;if(typeof e!="string")return!1;if(Array.isArray(t)){var i=!0;try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;if(Array.isArray(a))return!1;if(typeof a=="object")i=i&&bu(a);else if(!["number","string"].includes(typeof a))return!1;if(!i)return!1}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}}else{if(t==null)return!1;if(typeof t=="object")return bu(t);if(!["number","string","boolean"].includes(typeof t))return!1}return!0},Dn=(function(){function e(){this._propertySet=new Set,this._properties={}}return e.prototype.getUserProperties=function(){return D({},this._properties)},e.prototype.set=function(t,r){return this._safeSet(Me.SET,t,r),this},e.prototype.setOnce=function(t,r){return this._safeSet(Me.SET_ONCE,t,r),this},e.prototype.append=function(t,r){return this._safeSet(Me.APPEND,t,r),this},e.prototype.prepend=function(t,r){return this._safeSet(Me.PREPEND,t,r),this},e.prototype.postInsert=function(t,r){return this._safeSet(Me.POSTINSERT,t,r),this},e.prototype.preInsert=function(t,r){return this._safeSet(Me.PREINSERT,t,r),this},e.prototype.remove=function(t,r){return this._safeSet(Me.REMOVE,t,r),this},e.prototype.add=function(t,r){return this._safeSet(Me.ADD,t,r),this},e.prototype.unset=function(t){return this._safeSet(Me.UNSET,t,Sf),this},e.prototype.clearAll=function(){return this._properties={},this._properties[Me.CLEAR_ALL]=Sf,this},e.prototype._safeSet=function(t,r,n){if(this._validate(t,r,n)){var i=this._properties[t];return i===void 0&&(i={},this._properties[t]=i),i[r]=n,this._propertySet.add(r),!0}return!1},e.prototype._validate=function(t,r,n){return this._properties[Me.CLEAR_ALL]!==void 0||this._propertySet.has(r)?!1:t===Me.ADD?typeof n=="number":t!==Me.UNSET&&t!==Me.REMOVE?Fp(r,n):!0},e})(),Me;(function(e){e.SET="$set",e.SET_ONCE="$setOnce",e.ADD="$add",e.APPEND="$append",e.PREPEND="$prepend",e.REMOVE="$remove",e.PREINSERT="$preInsert",e.POSTINSERT="$postInsert",e.UNSET="$unset",e.CLEAR_ALL="$clearAll"})(Me||(Me={}));var jw=[Me.CLEAR_ALL,Me.UNSET,Me.SET,Me.SET_ONCE,Me.ADD,Me.APPEND,Me.PREPEND,Me.PREINSERT,Me.POSTINSERT,Me.REMOVE],qw="Event tracked successfully",Vw="Unexpected error occurred",$w="Event rejected due to exceeded retry count",Ww="Event skipped due to optOut config",Gw="Event rejected due to missing API key",Kw="Invalid API key",zw="Client not initialized",je;(function(e){e.Unknown="unknown",e.Skipped="skipped",e.Success="success",e.RateLimit="rate_limit",e.PayloadTooLarge="payload_too_large",e.Invalid="invalid",e.Failed="failed",e.Timeout="Timeout",e.SystemError="SystemError"})(je||(je={}));var Un=function(e,t,r){return t===void 0&&(t=0),r===void 0&&(r=je.Unknown),{event:e,code:t,message:r}},le=function(){var e="ampIntegrationContext";if(typeof globalThis<"u"&&typeof globalThis[e]<"u")return globalThis[e];if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},Xw=function(e){return e?(e^Math.random()*16>>e/4).toString(16):(String(1e7)+String(-1e3)+String(-4e3)+String(-8e3)+String(-1e11)).replace(/[018]/g,Qt)},Lf=Ee([],oe(Array(256).keys()),!1).map(function(e){return e.toString(16).padStart(2,"0")}),Qt=function(e){var t,r=le();if(!(!((t=r?.crypto)===null||t===void 0)&&t.getRandomValues))return Xw(e);var n=r.crypto.getRandomValues(new Uint8Array(16));return n[6]=n[6]&15|64,n[8]=n[8]&63|128,Ee([],oe(n.entries()),!1).map(function(i){var o=oe(i,2),s=o[0],a=o[1];return[4,6,8,10].includes(s)?"-".concat(Lf[a]):Lf[a]}).join("")},Yw=(function(){function e(t){this.client=t,this.queue=[],this.applying=!1,this.plugins=[],this._optOutListeners=[]}return e.prototype.register=function(t,r){var n,i;return x(this,void 0,void 0,function(){return O(this,function(o){switch(o.label){case 0:return this.plugins.some(function(s){return s.name===t.name})?(this.loggerProvider.warn("Plugin with name ".concat(t.name," already exists, skipping registration")),[2]):(t.name===void 0&&(t.name=Qt(),this.loggerProvider.warn(`Plugin name is undefined. - Generating a random UUID for plugin name: `.concat(t.name,`. - Set a name for the plugin to prevent it from being added multiple times.`))),t.type=(n=t.type)!==null&&n!==void 0?n:"enrichment",[4,(i=t.setup)===null||i===void 0?void 0:i.call(t,r,this.client)]);case 1:return o.sent(),this.plugins.push(t),[2]}})})},e.prototype.deregister=function(t,r){var n;return x(this,void 0,void 0,function(){var i,o;return O(this,function(s){switch(s.label){case 0:return i=this.plugins.findIndex(function(a){return a.name===t}),i===-1?(r.loggerProvider.warn("Plugin with name ".concat(t," does not exist, skipping deregistration")),[2]):(o=this.plugins[i],this.plugins.splice(i,1),[4,(n=o.teardown)===null||n===void 0?void 0:n.call(o)]);case 1:return s.sent(),[2]}})})},e.prototype.reset=function(t){this._clearOptOutListeners(),this.applying=!1;var r=this.plugins;r.map(function(n){var i;return(i=n.teardown)===null||i===void 0?void 0:i.call(n)}),this.plugins=[],this.client=t},e.prototype.push=function(t){var r=this;return new Promise(function(n){r.queue.push([t,n]),r.scheduleApply(0)})},e.prototype.scheduleApply=function(t){var r=this;this.applying||(this.applying=!0,setTimeout(function(){r.apply(r.queue.shift()).then(function(){r.applying=!1,r.queue.length>0&&r.scheduleApply(0)})},t))},e.prototype.apply=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u,h,v,c,l,f,d,h,v,p,b,E,m,g,y,_;return O(this,function(S){switch(S.label){case 0:if(!t)return[2];r=oe(t,1),n=r[0],i=oe(t,2),o=i[1],this.loggerProvider.log("Timeline.apply: Initial event",n),s=this.plugins.filter(function(A){return A.type==="before"}),S.label=1;case 1:S.trys.push([1,6,7,8]),a=ce(s),u=a.next(),S.label=2;case 2:return u.done?[3,5]:(h=u.value,h.execute?[4,h.execute(D({},n))]:[3,4]);case 3:if(v=S.sent(),v===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by before plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=v,this.loggerProvider.log("Timeline.apply: Event after before plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),S.label=4;case 4:return u=a.next(),[3,2];case 5:return[3,8];case 6:return c=S.sent(),m={error:c},[3,8];case 7:try{u&&!u.done&&(g=a.return)&&g.call(a)}finally{if(m)throw m.error}return[7];case 8:l=this.plugins.filter(function(A){return A.type==="enrichment"||A.type===void 0}),S.label=9;case 9:S.trys.push([9,14,15,16]),f=ce(l),d=f.next(),S.label=10;case 10:return d.done?[3,13]:(h=d.value,h.execute?[4,h.execute(D({},n))]:[3,12]);case 11:if(v=S.sent(),v===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by enrichment plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=v,this.loggerProvider.log("Timeline.apply: Event after enrichment plugin '".concat(String(h.name),"', event: ").concat(JSON.stringify(n))),S.label=12;case 12:return d=f.next(),[3,10];case 13:return[3,16];case 14:return p=S.sent(),y={error:p},[3,16];case 15:try{d&&!d.done&&(_=f.return)&&_.call(f)}finally{if(y)throw y.error}return[7];case 16:return b=this.plugins.filter(function(A){return A.type==="destination"}),this.loggerProvider.log("Timeline.apply: Final event before destinations, event: ".concat(JSON.stringify(n))),E=b.map(function(A){var I=D({},n);return A.execute(I).catch(function(C){return Un(I,0,String(C))})}),Promise.all(E).then(function(A){var I=oe(A,1),C=I[0],R=C||Un(n,100,"Event not tracked, no destination plugins on the instance");o(R)}),[2]}})})},e.prototype.flush=function(){return x(this,void 0,void 0,function(){var t,r,n,i=this;return O(this,function(o){switch(o.label){case 0:return t=this.queue,this.queue=[],[4,Promise.all(t.map(function(s){return i.apply(s)}))];case 1:return o.sent(),r=this.plugins.filter(function(s){return s.type==="destination"}),n=r.map(function(s){return s.flush&&s.flush()}),[4,Promise.all(n)];case 2:return o.sent(),[2]}})})},e.prototype.addOptOutListener=function(t){this._optOutListeners.push(t)},e.prototype._clearOptOutListeners=function(){this._optOutListeners=[]},e.prototype.onIdentityChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onIdentityChanged)===null||n===void 0||n.call(r,t)})},e.prototype.onSessionIdChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onSessionIdChanged)===null||n===void 0||n.call(r,t)})},e.prototype.onOptOutChanged=function(t){this.plugins.forEach(function(r){var n;(n=r.onOptOutChanged)===null||n===void 0||n.call(r,t)}),this._callOptOutListeners(t)},e.prototype._callOptOutListeners=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u;return O(this,function(c){switch(c.label){case 0:c.trys.push([0,7,8,9]),r=ce(this._optOutListeners),n=r.next(),c.label=1;case 1:if(n.done)return[3,6];i=n.value,c.label=2;case 2:return c.trys.push([2,4,,5]),[4,i(t)];case 3:return c.sent(),[3,5];case 4:return o=c.sent(),this.loggerProvider.error("Error calling optOut listener",o),[3,5];case 5:return n=r.next(),[3,1];case 6:return[3,9];case 7:return s=c.sent(),a={error:s},[3,9];case 8:try{n&&!n.done&&(u=r.return)&&u.call(r)}finally{if(a)throw a.error}return[7];case 9:return[2]}})})},e.prototype.onReset=function(){this.plugins.forEach(function(t){var r;(r=t.onReset)===null||r===void 0||r.call(t)})},e})(),Jw=function(e,t,r){var n=typeof e=="string"?{event_type:e}:e;return D(D(D({},n),r),t&&{event_properties:t})},Tl=function(e,t){var r=D(D({},t),{event_type:_t.IDENTIFY,user_properties:e.getUserProperties()});return r},Qw=function(e,t,r,n){var i,o=D(D({},n),{event_type:_t.GROUP_IDENTIFY,group_properties:r.getUserProperties(),groups:(i={},i[e]=t,i)});return o},Zw=function(e,t,r){var n,i=new Dn;i.set(e,t);var o=D(D({},r),{event_type:_t.IDENTIFY,user_properties:i.getUserProperties(),groups:(n={},n[e]=t,n)});return o},eT=function(e,t){return D(D({},t),{event_type:_t.REVENUE,event_properties:e.getEventProperties()})},tt=function(e){return{promise:e||Promise.resolve()}},tT=(function(){function e(t){t===void 0&&(t="$default"),this.initializing=!1,this.isReady=!1,this.q=[],this.dispatchQ=[],this.logEvent=this.track.bind(this),this.timeline=new Yw(this),this.name=t}return e.prototype._init=function(t){return x(this,void 0,void 0,function(){return O(this,function(r){switch(r.label){case 0:return this.config=t,this.timeline.reset(this),this.timeline.loggerProvider=this.config.loggerProvider,[4,this.runQueuedFunctions("q")];case 1:return r.sent(),this.isReady=!0,[2]}})})},e.prototype.runQueuedFunctions=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:r=this[t],this[t]=[],l.label=1;case 1:l.trys.push([1,8,9,10]),n=ce(r),i=n.next(),l.label=2;case 2:return i.done?[3,7]:(o=i.value,s=o(),s&&"promise"in s?[4,s.promise]:[3,4]);case 3:return l.sent(),[3,6];case 4:return[4,s];case 5:l.sent(),l.label=6;case 6:return i=n.next(),[3,2];case 7:return[3,10];case 8:return a=l.sent(),u={error:a},[3,10];case 9:try{i&&!i.done&&(c=n.return)&&c.call(n)}finally{if(u)throw u.error}return[7];case 10:return this[t].length?[4,this.runQueuedFunctions(t)]:[3,12];case 11:l.sent(),l.label=12;case 12:return[2]}})})},e.prototype.track=function(t,r,n){var i=Jw(t,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),tt(this.dispatch(i))},e.prototype.identify=function(t,r){var n=Tl(t,r);return this.userProperties=this.getOperationAppliedUserProperties(n.user_properties),tt(this.dispatch(n))},e.prototype.groupIdentify=function(t,r,n,i){var o=Qw(t,r,n,i);return tt(this.dispatch(o))},e.prototype.setGroup=function(t,r,n){var i=Zw(t,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),tt(this.dispatch(i))},e.prototype.revenue=function(t,r){var n=eT(t,r);return tt(this.dispatch(n))},e.prototype.add=function(t){return this.isReady?this._addPlugin(t):(this.q.push(this._addPlugin.bind(this,t)),tt())},e.prototype._addPlugin=function(t){return tt(this.timeline.register(t,this.config))},e.prototype.remove=function(t){return this.isReady?this._removePlugin(t):(this.q.push(this._removePlugin.bind(this,t)),tt())},e.prototype._removePlugin=function(t){return tt(this.timeline.deregister(t,this.config))},e.prototype.dispatchWithCallback=function(t,r){if(!this.isReady)return r(Un(t,0,zw));this.process(t).then(r)},e.prototype.dispatch=function(t){return x(this,void 0,void 0,function(){var r=this;return O(this,function(n){return this.isReady?[2,this.process(t)]:[2,new Promise(function(i){r.dispatchQ.push(r.dispatchWithCallback.bind(r,t,i))})]})})},e.prototype.getOperationAppliedUserProperties=function(t){var r,n=(r=this.userProperties)!==null&&r!==void 0?r:{},i=D({},n);if(t===void 0)return i;var o={};return Object.keys(t).forEach(function(s){Object.values(hr).includes(s)||(o[s]=t[s])}),jw.forEach(function(s){if(Object.keys(t).includes(s)){var a=t[s];switch(s){case hr.CLEAR_ALL:Object.keys(i).forEach(function(u){delete i[u]});break;case hr.UNSET:Object.keys(a).forEach(function(u){delete i[u]});break;case hr.SET:Object.assign(i,a);break}}}),Object.assign(i,o),i},e.prototype.process=function(t){return x(this,void 0,void 0,function(){var i,r,n,i;return O(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),this.config.optOut?[2,Un(t,0,Ww)]:(t.event_type===_t.IDENTIFY&&this.timeline.onIdentityChanged({userProperties:this.userProperties}),[4,this.timeline.push(t)]);case 1:return i=o.sent(),i.code===200?this.config.loggerProvider.log(i.message):i.code===100?this.config.loggerProvider.warn(i.message):this.config.loggerProvider.error(i.message),[2,i];case 2:return r=o.sent(),n=String(r),this.config.loggerProvider.error(n),i=Un(t,0,n),[2,i];case 3:return[2]}})})},e.prototype.setOptOut=function(t){if(!this.isReady){this.q.push(this._setOptOut.bind(this,!!t));return}this._setOptOut(t)},e.prototype._setOptOut=function(t){this.config.optOut!==t&&(this.config.optOut=!!t,this.timeline.onOptOutChanged(t))},e.prototype.flush=function(){return tt(this.timeline.flush())},e.prototype.plugin=function(t){var r=this.timeline.plugins.find(function(n){return n.name===t});if(r===void 0){this.config.loggerProvider.debug("Cannot find plugin with name ".concat(t));return}return r},e.prototype.plugins=function(t){return this.timeline.plugins.filter(function(r){return r instanceof t})},e})(),Hp=(function(){function e(){this.productId="",this.quantity=1,this.price=0}return e.prototype.setProductId=function(t){return this.productId=t,this},e.prototype.setQuantity=function(t){return t>0&&(this.quantity=t),this},e.prototype.setPrice=function(t){return this.price=t,this},e.prototype.setRevenueType=function(t){return this.revenueType=t,this},e.prototype.setCurrency=function(t){return this.currency=t,this},e.prototype.setRevenue=function(t){return this.revenue=t,this},e.prototype.setReceipt=function(t){return this.receipt=t,this},e.prototype.setReceiptSig=function(t){return this.receiptSig=t,this},e.prototype.setEventProperties=function(t){try{var r=JSON.parse(JSON.stringify(t));bu(r)&&(this.properties=r)}catch{}return this},e.prototype.getEventProperties=function(){var t=this.properties?D({},this.properties):{};return t[Ut.REVENUE_PRODUCT_ID]=this.productId,t[Ut.REVENUE_QUANTITY]=this.quantity,t[Ut.REVENUE_PRICE]=this.price,t[Ut.REVENUE_TYPE]=this.revenueType,t[Ut.REVENUE_CURRENCY]=this.currency,t[Ut.REVENUE]=this.revenue,t[Ut.RECEIPT]=this.receipt,t[Ut.RECEIPT_SIG]=this.receiptSig,t},e})(),Ut;(function(e){e.REVENUE_PRODUCT_ID="$productId",e.REVENUE_QUANTITY="$quantity",e.REVENUE_PRICE="$price",e.REVENUE_TYPE="$revenueType",e.REVENUE_CURRENCY="$currency",e.REVENUE="$revenue",e.RECEIPT="$receipt",e.RECEIPT_SIG="$receiptSig"})(Ut||(Ut={}));var rT=function(e,t){var r=Math.max(t,1);return e.reduce(function(n,i,o){var s=Math.floor(o/r);return n[s]||(n[s]=[]),n[s].push(i),n},[])},ot;(function(e){e[e.None=0]="None",e[e.Error=1]="Error",e[e.Warn=2]="Warn",e[e.Verbose=3]="Verbose",e[e.Debug=4]="Debug"})(ot||(ot={}));var yo="Amplitude Logger ",Kn=(function(){function e(){this.logLevel=ot.None}return e.prototype.disable=function(){this.logLevel=ot.None},e.prototype.enable=function(t){t===void 0&&(t=ot.Warn),this.logLevel=t},e.prototype.log=function(){for(var t=[],r=0;r=200&&e<300}var Fo=function(e){e===void 0&&(e=0);var t=new Error().stack||"";return t.split(` -`).slice(2+e).map(function(r){return r.trim()})},Re=function(e){return function(){var t=D({},e.config),r=t.loggerProvider,n=t.logLevel;return{logger:r,logLevel:n}}},oT=function(e,t){var r,n;t=t.replace(/\[(\w+)\]/g,".$1"),t=t.replace(/^\./,"");try{for(var i=ce(t.split(".")),o=i.next();!o.done;o=i.next()){var s=o.value;if(s in e)e=e[s];else return}}catch(a){r={error:a}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return e},Pe=function(e,t){return function(){var r,n,i={};try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;i[a]=oT(e,a)}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return i}},_e=function(e,t,r,n,i){return i===void 0&&(i=null),function(){for(var o=[],s=0;s0&&Promise.all(n.map(function(s){return i.execute(s)})).catch(),[2,Promise.resolve(void 0)]}})})},e.prototype.execute=function(t){var r=this;return t.insert_id||(t.insert_id=Qt()),new Promise(function(n){var i={event:t,attempts:0,callback:function(o){return n(o)},timeout:0};r.queue.push(i),r.schedule(r.config.flushIntervalMillis),r.saveEvents()})},e.prototype.removeEventsExceedFlushMaxRetries=function(t){var r=this;return t.filter(function(n){return n.attempts+=1,n.attemptsthis.scheduledTimeout)){this.scheduleId&&clearTimeout(this.scheduleId),this.scheduledTimeout=t,this.scheduleId=setTimeout(function(){r.queue=r.queue.map(function(n){return n.timeout=0,n}),r.flush(!0)},t);return}},e.prototype.resetSchedule=function(){this.scheduleId=null,this.scheduledTimeout=0},e.prototype.flush=function(t){return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var r,n,i,o=this;return O(this,function(s){switch(s.label){case 0:return this.config.offline?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush while offline."),[2]):this.flushId?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush because previous flush has not resolved."),[2]):(this.flushId=this.scheduleId,this.resetSchedule(),r=[],n=[],this.queue.forEach(function(a){return a.timeout===0?r.push(a):n.push(a)}),i=rT(r,this.config.flushQueueSize),[4,i.reduce(function(a,u){return x(o,void 0,void 0,function(){return O(this,function(c){switch(c.label){case 0:return[4,a];case 1:return c.sent(),[4,this.send(u,t)];case 2:return[2,c.sent()]}})})},Promise.resolve())]);case 1:return s.sent(),this.flushId=null,this.scheduleEvents(this.queue),[2]}})})},e.prototype.send=function(t,r){var n;return r===void 0&&(r=!0),x(this,void 0,void 0,function(){var i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:if(!this.config.apiKey)return[2,this.fulfillRequest(t,400,Gw)];i={api_key:this.config.apiKey,events:t.map(function(f){var d=f.event;d.extra;var h=hs(d,["extra"]);return h}),options:{min_id_length:this.config.minIdLength},client_upload_time:new Date().toISOString(),request_metadata:this.config.requestMetadata},this.config.requestMetadata=new iT,l.label=1;case 1:return l.trys.push([1,3,,4]),o=jp(this.config.serverUrl,this.config.serverZone,this.config.useBatch).serverUrl,s=aT(o,this.config.enableRequestBodyCompression),[4,this.config.transportProvider.send(o,i,this.config._enableRequestBodyCompressionExperimental&&s)];case 2:return a=l.sent(),a===null?(this.fulfillRequest(t,0,Vw),[2]):r?(this.handleResponse(a,t),[3,4]):("body"in a?this.fulfillRequest(t,a.statusCode,"".concat(a.status,": ").concat(ui(a))):this.fulfillRequest(t,a.statusCode,a.status),[2]);case 3:return u=l.sent(),c=uT(u),this.config.loggerProvider.error(c),(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful.from.catch.error",{events:t.map(function(f){return f.event.event_type}),message:c,stack_trace:Fo()}),this.handleResponse({status:je.Failed,statusCode:0},t),[3,4];case 4:return[2]}})})},e.prototype.handleResponse=function(t,r){var n;Eu(t.statusCode)||(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful",{events:r.map(function(o){return o.event.event_type}),code:t.statusCode,status:t.status,body:ui(t),stack_trace:Fo()});var i=t.status;switch(i){case je.Success:{this.handleSuccessResponse(t,r);break}case je.Invalid:{this.handleInvalidResponse(t,r);break}case je.PayloadTooLarge:{this.handlePayloadTooLargeResponse(t,r);break}case je.RateLimit:{this.handleRateLimitResponse(t,r);break}default:{this.config.loggerProvider.warn(`{code: 0, error: "Status '`.concat(i,"' provided for ").concat(r.length,' events"}')),this.handleOtherResponse(r);break}}},e.prototype.handleSuccessResponse=function(t,r){this.fulfillRequest(r,t.statusCode,qw)},e.prototype.handleInvalidResponse=function(t,r){var n=this;if(t.body.missingField||t.body.error.startsWith(Kw)){this.fulfillRequest(r,t.statusCode,t.body.error);return}var i=Ee(Ee(Ee(Ee([],oe(Object.values(t.body.eventsWithInvalidFields)),!1),oe(Object.values(t.body.eventsWithMissingFields)),!1),oe(Object.values(t.body.eventsWithInvalidIdLengths)),!1),oe(t.body.silencedEvents),!1).flat(),o=new Set(i),s=r.filter(function(u,c){if(o.has(c)){n.fulfillRequest([u],t.statusCode,t.body.error);return}return!0});s.length>0&&this.config.loggerProvider.warn(ui(t));var a=this.removeEventsExceedFlushMaxRetries(s);this.scheduleEvents(a)},e.prototype.handlePayloadTooLargeResponse=function(t,r){if(r.length===1){this.fulfillRequest(r,t.statusCode,t.body.error);return}this.config.loggerProvider.warn(ui(t)),this.config.flushQueueSize/=2;var n=this.removeEventsExceedFlushMaxRetries(r);this.scheduleEvents(n)},e.prototype.handleRateLimitResponse=function(t,r){var n=this,i=Object.keys(t.body.exceededDailyQuotaUsers),o=Object.keys(t.body.exceededDailyQuotaDevices),s=t.body.throttledEvents,a=new Set(i),u=new Set(o),c=new Set(s),l=r.filter(function(d,h){if(d.event.user_id&&a.has(d.event.user_id)||d.event.device_id&&u.has(d.event.device_id)){n.fulfillRequest([d],t.statusCode,t.body.error);return}return c.has(h)&&(d.timeout=n.throttleTimeout),!0});l.length>0&&this.config.loggerProvider.warn(ui(t));var f=this.removeEventsExceedFlushMaxRetries(l);this.scheduleEvents(f)},e.prototype.handleOtherResponse=function(t){var r=this,n=t.map(function(o){return o.timeout=o.attempts*r.retryTimeout,o}),i=this.removeEventsExceedFlushMaxRetries(n);this.scheduleEvents(i)},e.prototype.fulfillRequest=function(t,r,n){var i,o,s;Eu(r)?(s=this.diagnosticsClient)===null||s===void 0||s.increment("analytics.events.sent",t.length):((i=this.diagnosticsClient)===null||i===void 0||i.increment("analytics.events.dropped",t.length),(o=this.diagnosticsClient)===null||o===void 0||o.recordEvent("analytics.events.dropped",{events:t.map(function(a){return a.event.event_type}),code:r,message:n,stack_trace:Fo()})),this.removeEvents(t),t.forEach(function(a){return a.callback(Un(a.event,r,n))})},e.prototype.saveEvents=function(){if(this.config.storageProvider){var t=this.queue.map(function(r){return r.event});this.config.storageProvider.set(this.storageKey,t)}},e.prototype.removeEvents=function(t){this.queue=this.queue.filter(function(r){return!t.some(function(n){return n.event.insert_id===r.event.insert_id})}),this.saveEvents()},e})(),cT=(function(){function e(){}return e.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:fT(),platform:"Web",os:void 0,deviceModel:void 0}},e})(),fT=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},dT=(function(){function e(){this.queue=[]}return e.prototype.logEvent=function(t){this.receiver?this.receiver(t):this.queue.length<512&&this.queue.push(t)},e.prototype.setEventReceiver=function(t){this.receiver=t,this.queue.length>0&&(this.queue.forEach(function(r){t(r)}),this.queue=[])},e})(),Cr=function(){return Cr=Object.assign||function(t){for(var r,n=1,i=arguments.length;n=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Mf(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}var Bo=function(e,t){var r,n,i=["string","number","boolean","undefined"],o=typeof e,s=typeof t;if(o!==s)return!1;try{for(var a=Ho(i),u=a.next();!u.done;u=a.next()){var c=u.value;if(c===o)return e===t}}catch(b){r={error:b}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}if(e==null&&t==null)return!0;if(e==null||t==null||e.length!==t.length)return!1;var l=Array.isArray(e),f=Array.isArray(t);if(l!==f)return!1;if(l&&f){for(var d=0;de},Vp=function(e,t,r){return t===void 0&&(t=""),r===void 0&&(r=10),[Us,t,e.substring(0,r)].filter(Boolean).join("_")},ET=function(e){return"".concat(Us.toLowerCase(),"_").concat(e.substring(0,6))},_T=function(){var e,t,r,n;if(typeof navigator>"u")return"";var i=navigator.userLanguage;return(n=(r=(t=(e=navigator.languages)===null||e===void 0?void 0:e[0])!==null&&t!==void 0?t:navigator.language)!==null&&r!==void 0?r:i)!==null&&n!==void 0?n:""},ps=function(){var e,t=le();if(!(!((e=t?.location)===null||e===void 0)&&e.search))return{};var r=t.location.search.substring(1).split("&").filter(Boolean),n=r.reduce(function(i,o){var s=o.split("=",2),a=Df(s[0]),u=Df(s[1]);return u&&(i[a]=u),i},{});return n},Df=function(e){e===void 0&&(e="");try{return decodeURIComponent(e)}catch{return""}},_u=function(e,t){return!t||!t.length?!0:t.some(function(r){return typeof r=="string"?e===r:e.match(r)})},xr=function(e,t){var r=e;try{r=decodeURI(e)}catch(n){t?.error("Malformed URI sequence: ",n)}return r},Su=function(e){var t=0;if(e.length===0)return t;for(var r=0;r1&&((r=this.config.diagnosticsClient)===null||r===void 0||r.recordEvent("cookies.duplicate",{cookies:a.map(function(v){return v.domain})}),(n=this.config.diagnosticsClient)===null||n===void 0||n.increment("cookies.duplicate.occurrence.cookieStore"));try{for(u=ce(a),c=u.next();!c.done;c=u.next())if(l=c.value,Wp(l.domain,this.options.domain))return[2,l.value]}catch(v){f={error:v}}finally{try{c&&!c.done&&(d=u.return)&&d.call(u)}finally{if(f)throw f.error}}}h.label=3;case 3:return[3,5];case 4:return h.sent(),[3,5];case 5:return[2,this.getRawSync(t)]}})})},e.prototype.getRawSync=function(t){var r=this,n,i,o=le(),s=((i=(n=o?.document)===null||n===void 0?void 0:n.cookie.split("; "))!==null&&i!==void 0?i:[]).filter(function(c){return c.indexOf(t+"=")===0}),a=void 0,u=this.config.duplicateResolverFn;if(typeof u=="function"&&s.length>1&&(a=s.find(function(c){var l;try{var f=u(c.substring(t.length+1));return f||(l=r.config.diagnosticsClient)===null||l===void 0||l.increment("cookies.duplicate.occurrence.document.cookie"),f}catch{return!1}})),a||(a=s[0]),!!a)return a.substring(t.length+1)},e.prototype.set=function(t,r){return x(this,void 0,void 0,function(){return O(this,function(n){return this.setSync(t,r),[2]})})},e.prototype.setSync=function(t,r){var n;try{var i=(n=this.options.expirationDays)!==null&&n!==void 0?n:0,o=r!==null?i:-1,s=void 0;if(o){var a=new Date;a.setTime(a.getTime()+o*24*60*60*1e3),s=a}var u="".concat(t,"=").concat(btoa(encodeURIComponent(JSON.stringify(r))));s&&(u+="; expires=".concat(s.toUTCString())),u+="; path=/",this.options.domain&&(u+="; domain=".concat(this.options.domain)),this.options.secure&&(u+="; Secure"),this.options.sameSite&&(u+="; SameSite=".concat(this.options.sameSite));var c=le();c?.document&&(c.document.cookie=u)}catch(f){var l=f instanceof Error?f.message:String(f);console.error("Amplitude Logger [Error]: Failed to set cookie for key: ".concat(t,". Error: ").concat(l))}},e.prototype.remove=function(t){return x(this,void 0,void 0,function(){return O(this,function(r){switch(r.label){case 0:return[4,this.set(t,null)];case 1:return r.sent(),[2]}})})},e.prototype.reset=function(){return x(this,void 0,void 0,function(){return O(this,function(t){return[2]})})},e.isDomainWritable=function(t){return x(this,void 0,void 0,function(){var r,n,i,o;return O(this,function(s){switch(s.label){case 0:if(e.cachedTlds[t])return[2,!0];r={domain:"."+t},n="AMP_TLDTEST",i=new e(r),s.label=1;case 1:return s.trys.push([1,3,,4]),[4,i.transaction(n,function(a){if(e.cachedTlds[t])return!0;try{a.set(1);var u=!!a.get();return u&&(e.cachedTlds[t]=!0),u}finally{a.set(null)}})];case 2:return o=s.sent(),[2,!!o];case 3:return s.sent(),[2,!1];case 4:return[2]}})})},e.prototype.transaction=function(t,r){return x(this,void 0,void 0,function(){var n,i,o=this;return O(this,function(s){switch(s.label){case 0:if(n=ST(),i=function(){var a={get:function(){return o.getSync(t)},set:function(u){return o.setSync(t,u)}};return r(a)},!n)return[2,i()];s.label=1;case 1:return s.trys.push([1,3,,4]),[4,n.request("com.amplitude:cookie-lock:".concat(t),i)];case 2:return[2,s.sent()];case 3:return s.sent(),[2,i()];case 4:return[2]}})})},e.cachedTlds={},e})(),wT=function(e){try{return decodeURIComponent(atob(e))}catch{return}},TT=function(e){try{return decodeURIComponent(atob(decodeURIComponent(e)))}catch{return}},$p=function(e){var t;return(t=wT(e))!==null&&t!==void 0?t:TT(e)},Wp=function(e,t){if(e===""&&t==="")return!0;if(!e||!t)return!1;var r=e.startsWith(".")?e.substring(1):e,n=t.startsWith(".")?t.substring(1):t;return r.toLowerCase()===n.toLowerCase()},Ff=function(e,t,r){return t===void 0&&(t=""),r===void 0&&(r=10),[Us,t,e.substring(0,r)].filter(Boolean).join("_")},Hs=(function(){function e(t){this.storage=t}return e.prototype.isEnabled=function(){return x(this,void 0,void 0,function(){var t,r,n,i;return O(this,function(o){switch(o.label){case 0:if(!this.storage)return[2,!1];t=String(Date.now()),r=new e(this.storage),n="AMP_TEST",o.label=1;case 1:return o.trys.push([1,4,5,7]),[4,r.set(n,t)];case 2:return o.sent(),[4,r.get(n)];case 3:return i=o.sent(),[2,i===t];case 4:return o.sent(),[2,!1];case 5:return[4,r.remove(n)];case 6:return o.sent(),[7];case 7:return[2]}})})},e.prototype.get=function(t){return x(this,void 0,void 0,function(){var r;return O(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,this.getRaw(t)];case 1:return r=n.sent(),r?[2,JSON.parse(r)]:[2,void 0];case 2:return n.sent(),console.error("[Amplitude] Error: Could not get value from storage"),[2,void 0];case 3:return[2]}})})},e.prototype.getRaw=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){return[2,((r=this.storage)===null||r===void 0?void 0:r.getItem(t))||void 0]})})},e.prototype.set=function(t,r){var n;return x(this,void 0,void 0,function(){return O(this,function(i){try{(n=this.storage)===null||n===void 0||n.setItem(t,JSON.stringify(r))}catch{}return[2]})})},e.prototype.remove=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){try{(r=this.storage)===null||r===void 0||r.removeItem(t)}catch{}return[2]})})},e.prototype.reset=function(){var t;return x(this,void 0,void 0,function(){return O(this,function(r){try{(t=this.storage)===null||t===void 0||t.clear()}catch{}return[2]})})},e})(),AT=10,IT=1,we={TAGS:"tags",COUNTERS:"counters",HISTOGRAMS:"histograms",EVENTS:"events",INTERNAL:"internal"},Hf={LAST_FLUSH_TIMESTAMP:"last_flush_timestamp"},Bf=(function(){function e(t,r){this.dbPromise=null,this.logger=r,this.dbName="AMP_diagnostics_".concat(t.substring(0,10))}return e.isSupported=function(){var t;return((t=le())===null||t===void 0?void 0:t.indexedDB)!==void 0},e.prototype.getDB=function(){return x(this,void 0,void 0,function(){return O(this,function(t){return this.dbPromise||(this.dbPromise=this.openDB()),[2,this.dbPromise]})})},e.prototype.openDB=function(){var t=this;return new Promise(function(r,n){var i=indexedDB.open(t.dbName,IT);i.onerror=function(){t.dbPromise=null,n(new Error("Failed to open IndexedDB"))},i.onsuccess=function(){var o=i.result;o.onclose=function(){t.dbPromise=null,t.logger.debug("DiagnosticsStorage: DB connection closed.")},o.onerror=function(s){t.logger.debug("DiagnosticsStorage: A global database error occurred.",s),o.close()},r(o)},i.onupgradeneeded=function(o){var s=o.target.result;t.createTables(s)}})},e.prototype.createTables=function(t){if(t.objectStoreNames.contains(we.TAGS)||t.createObjectStore(we.TAGS,{keyPath:"key"}),t.objectStoreNames.contains(we.COUNTERS)||t.createObjectStore(we.COUNTERS,{keyPath:"key"}),t.objectStoreNames.contains(we.HISTOGRAMS)||t.createObjectStore(we.HISTOGRAMS,{keyPath:"key"}),!t.objectStoreNames.contains(we.EVENTS)){var r=t.createObjectStore(we.EVENTS,{keyPath:"id",autoIncrement:!0});r.createIndex("time_idx","time",{unique:!1})}t.objectStoreNames.contains(we.INTERNAL)||t.createObjectStore(we.INTERNAL,{keyPath:"key"})},e.prototype.setTags=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.TAGS],"readwrite"),i=n.objectStore(we.TAGS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to set tags",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.put({key:d,value:h});v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to set tag",d,h,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to set tags",o),[3,3];case 3:return[2]}})})},e.prototype.incrementCounters=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.COUNTERS],"readwrite"),i=n.objectStore(we.COUNTERS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to increment counters",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.get(d);v.onsuccess=function(){var p=v.result,b=p?p.value:0,E=i.put({key:d,value:b+h});E.onerror=function(m){s.logger.debug("DiagnosticsStorage: Failed to update counter",d,m)}},v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to read existing counter",d,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to increment counters",o),[3,3];case 3:return[2]}})})},e.prototype.setHistogramStats=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),Object.entries(t).length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.HISTOGRAMS],"readwrite"),i=n.objectStore(we.HISTOGRAMS),[2,new Promise(function(u){var c=Object.entries(t);n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to set histogram stats",l),u()},c.forEach(function(l){var f=oe(l,2),d=f[0],h=f[1],v=i.get(d);v.onsuccess=function(){var p=v.result,b;p?b={key:d,count:p.count+h.count,min:Math.min(p.min,h.min),max:Math.max(p.max,h.max),sum:p.sum+h.sum}:b={key:d,count:h.count,min:h.min,max:h.max,sum:h.sum};var E=i.put(b);E.onerror=function(m){s.logger.debug("DiagnosticsStorage: Failed to set histogram stats",d,m)}},v.onerror=function(p){s.logger.debug("DiagnosticsStorage: Failed to read existing histogram stats",d,p)}})})];case 2:return o=a.sent(),this.logger.debug("DiagnosticsStorage: Failed to set histogram stats",o),[3,3];case 3:return[2]}})})},e.prototype.addEventRecords=function(t){return x(this,void 0,void 0,function(){var r,n,i,o,s=this;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,2,,3]),t.length===0?[2]:[4,this.getDB()];case 1:return r=a.sent(),n=r.transaction([we.EVENTS],"readwrite"),i=n.objectStore(we.EVENTS),[2,new Promise(function(u){n.oncomplete=function(){u()},n.onabort=function(l){s.logger.debug("DiagnosticsStorage: Failed to add event records",l),u()};var c=i.count();c.onsuccess=function(){var l=c.result,f=Math.max(0,AT-l);f=ga){this.logger.debug("DiagnosticsClient: Early return setTags as reaching memory limit");return}this.inMemoryTags[t]=r,this.startTimersIfNeeded()}},e.prototype.increment=function(t,r){if(r===void 0&&(r=1),!!this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryCounters).length>=ga){this.logger.debug("DiagnosticsClient: Early return increment as reaching memory limit");return}this.inMemoryCounters[t]=(this.inMemoryCounters[t]||0)+r,this.startTimersIfNeeded()}},e.prototype.recordHistogram=function(t,r){if(this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryHistograms).length>=ga){this.logger.debug("DiagnosticsClient: Early return recordHistogram as reaching memory limit");return}var n=this.inMemoryHistograms[t];n?(n.count+=1,n.min=Math.min(n.min,r),n.max=Math.max(n.max,r),n.sum+=r):this.inMemoryHistograms[t]={count:1,min:r,max:r,sum:r},this.startTimersIfNeeded()}},e.prototype.recordEvent=function(t,r){if(this.isStorageAndTrackEnabled()){if(this.inMemoryEvents.length>=DT){this.logger.debug("DiagnosticsClient: Early return recordEvent as reaching memory limit");return}this.inMemoryEvents.push({event_name:t,time:Date.now(),event_properties:r}),this.startTimersIfNeeded()}},e.prototype.startTimersIfNeeded=function(){var t=this;this.saveTimer||(this.saveTimer=setTimeout(function(){t.saveAllDataToStorage().catch(function(r){t.logger.debug("DiagnosticsClient: Failed to save all data to storage",r)}).finally(function(){t.saveTimer=null})},NT)),this.flushTimer||(this.flushTimer=setTimeout(function(){t._flush().catch(function(r){t.logger.debug("DiagnosticsClient: Failed to flush",r)}).finally(function(){t.flushTimer=null})},bo))},e.prototype.saveAllDataToStorage=function(){return x(this,void 0,void 0,function(){var t,r,n,i;return O(this,function(o){switch(o.label){case 0:return this.storage?(t=D({},this.inMemoryTags),r=D({},this.inMemoryCounters),n=D({},this.inMemoryHistograms),i=Ee([],oe(this.inMemoryEvents),!1),this.inMemoryEvents=[],this.inMemoryTags={},this.inMemoryCounters={},this.inMemoryHistograms={},[4,Promise.all([this.storage.setTags(t),this.storage.incrementCounters(r),this.storage.setHistogramStats(n),this.storage.addEventRecords(i)])]):[2];case 1:return o.sent(),[2]}})})},e.prototype._flush=function(){return x(this,void 0,void 0,function(){var t,r,n,i,o,s,a,u,c,l;return O(this,function(f){switch(f.label){case 0:return this.storage?[4,this.saveAllDataToStorage()]:[2];case 1:return f.sent(),this.saveTimer=null,this.flushTimer=null,[4,this.storage.getAllAndClear()];case 2:return t=f.sent(),r=t.tags,n=t.counters,i=t.histogramStats,o=t.events,this.storage.setLastFlushTimestamp(Date.now()),s={},r.forEach(function(d){s[d.key]=d.value}),a={},n.forEach(function(d){a[d.key]=d.value}),u={},i.forEach(function(d){u[d.key]={count:d.count,min:d.min,max:d.max,avg:Math.round(d.sum/d.count*100)/100}}),c=o.map(function(d){return{event_name:d.event_name,time:d.time,event_properties:d.event_properties}}),Object.keys(a).length===0&&Object.keys(u).length===0&&c.length===0?[2]:(l={tags:s,histogram:u,counters:a,events:c},this.fetch(l),[2])}})})},e.prototype.fetch=function(t){return x(this,void 0,void 0,function(){var r,n;return O(this,function(i){switch(i.label){case 0:if(i.trys.push([0,2,,3]),!le())throw new Error("DiagnosticsClient: Fetch is not supported");return[4,fetch(this.serverUrl,{method:"POST",headers:{"X-ApiKey":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(t)})];case 1:return r=i.sent(),r.ok?(this.logger.debug("DiagnosticsClient: Successfully sent diagnostics data"),[3,3]):(this.logger.debug("DiagnosticsClient: Failed to send diagnostics data."),[2]);case 2:return n=i.sent(),this.logger.debug("DiagnosticsClient: Failed to send diagnostics data. ",n),[3,3];case 3:return[2]}})})},e.prototype.initializeFlushInterval=function(){return x(this,void 0,void 0,function(){var t,r,n;return O(this,function(i){switch(i.label){case 0:return this.storage?(t=Date.now(),[4,this.storage.getLastFlushTimestamp()]):[2];case 1:return r=i.sent()||-1,r===-1?(this.storage.setLastFlushTimestamp(t),this._setFlushTimer(bo),[2]):(n=t-r,n>=bo?(this._flush(),[2]):(this._setFlushTimer(bo-n),[2]))}})})},e.prototype._setFlushTimer=function(t){var r=this;this.flushTimer=setTimeout(function(){r._flush().catch(function(n){r.logger.debug("DiagnosticsClient: Failed to flush",n)}).finally(function(){r.flushTimer=null})},t)},e.prototype._setSampleRate=function(t){this.logger.debug("DiagnosticsClient: Setting sample rate to",t),this.config.sampleRate=t,this.shouldTrack=Uf(this.startTimestamp,this.config.sampleRate)&&this.config.enabled,this.logger.debug("DiagnosticsClient: Should track is",this.shouldTrack)},e})(),ro=(function(){function e(){}return e.prototype.send=function(t,r,n){return Promise.resolve(null)},e.prototype.buildResponse=function(t){var r,n,i,o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A;if(typeof t!="object")return null;var I=t.code||0,C=this.buildStatus(I);switch(C){case je.Success:return{status:C,statusCode:I,body:{eventsIngested:(r=t.events_ingested)!==null&&r!==void 0?r:0,payloadSizeBytes:(n=t.payload_size_bytes)!==null&&n!==void 0?n:0,serverUploadTime:(i=t.server_upload_time)!==null&&i!==void 0?i:0}};case je.Invalid:return{status:C,statusCode:I,body:{error:(o=t.error)!==null&&o!==void 0?o:"",missingField:(s=t.missing_field)!==null&&s!==void 0?s:"",eventsWithInvalidFields:(a=t.events_with_invalid_fields)!==null&&a!==void 0?a:{},eventsWithMissingFields:(u=t.events_with_missing_fields)!==null&&u!==void 0?u:{},eventsWithInvalidIdLengths:(c=t.events_with_invalid_id_lengths)!==null&&c!==void 0?c:{},epsThreshold:(l=t.eps_threshold)!==null&&l!==void 0?l:0,exceededDailyQuotaDevices:(f=t.exceeded_daily_quota_devices)!==null&&f!==void 0?f:{},silencedDevices:(d=t.silenced_devices)!==null&&d!==void 0?d:[],silencedEvents:(h=t.silenced_events)!==null&&h!==void 0?h:[],throttledDevices:(v=t.throttled_devices)!==null&&v!==void 0?v:{},throttledEvents:(p=t.throttled_events)!==null&&p!==void 0?p:[]}};case je.PayloadTooLarge:return{status:C,statusCode:I,body:{error:(b=t.error)!==null&&b!==void 0?b:""}};case je.RateLimit:return{status:C,statusCode:I,body:{error:(E=t.error)!==null&&E!==void 0?E:"",epsThreshold:(m=t.eps_threshold)!==null&&m!==void 0?m:0,throttledDevices:(g=t.throttled_devices)!==null&&g!==void 0?g:{},throttledUsers:(y=t.throttled_users)!==null&&y!==void 0?y:{},exceededDailyQuotaDevices:(_=t.exceeded_daily_quota_devices)!==null&&_!==void 0?_:{},exceededDailyQuotaUsers:(S=t.exceeded_daily_quota_users)!==null&&S!==void 0?S:{},throttledEvents:(A=t.throttled_events)!==null&&A!==void 0?A:[]}};case je.Timeout:default:return{status:C,statusCode:I}}},e.prototype.buildStatus=function(t){return Eu(t)?je.Success:t===429?je.RateLimit:t===413?je.PayloadTooLarge:t===408?je.Timeout:t>=400&&t<500?je.Invalid:t>=500?je.Failed:je.Unknown},e})(),FT=(function(e){Mt(t,e);function t(r){r===void 0&&(r={});var n=e.call(this)||this;return n.customHeaders=r,n}return t.prototype.send=function(r,n){return x(this,void 0,void 0,function(){var i,o,s;return O(this,function(a){switch(a.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return i={headers:D({"Content-Type":"application/json",Accept:"*/*"},this.customHeaders),body:JSON.stringify(n),method:"POST"},[4,fetch(r,i)];case 1:return o=a.sent(),[4,o.text()];case 2:s=a.sent();try{return[2,this.buildResponse(JSON.parse(s))]}catch{return[2,this.buildResponse({code:o.status})]}return[2]}})})},t})(ro),Gp=2*1024;function Kp(){return typeof CompressionStream<"u"}function zp(e){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:if(t=CompressionStream,typeof t>"u")return[2,void 0];n.label=1;case 1:return n.trys.push([1,3,,4]),r=new Blob([e]).stream().pipeThrough(new t("gzip")),[4,new Response(r).arrayBuffer()];case 2:return[2,n.sent()];case 3:return n.sent(),[2,void 0];case 4:return[2]}})})}var HT=(function(){function e(t,r){this.key="AMP_remote_config_".concat(t.substring(0,10)),this.logger=r}return e.prototype.fetchConfig=function(){var t=null,r={remoteConfig:null,lastFetch:new Date};try{t=localStorage.getItem(this.key)}catch(i){return this.logger.debug("Remote config localstorage failed to access: ",i),Promise.resolve(r)}if(t===null)return this.logger.debug("Remote config localstorage gets null because the key does not exist"),Promise.resolve(r);try{var n=JSON.parse(t);return this.logger.debug("Remote config localstorage parsed successfully: ".concat(JSON.stringify(n))),Promise.resolve({remoteConfig:n.remoteConfig,lastFetch:new Date(n.lastFetch)})}catch(i){return this.logger.debug("Remote config localstorage failed to parse: ",i),localStorage.removeItem(this.key),Promise.resolve(r)}},e.prototype.setConfig=function(t){try{return localStorage.setItem(this.key,JSON.stringify(t)),this.logger.debug("Remote config localstorage set successfully."),Promise.resolve(!0)}catch(r){this.logger.debug("Remote config localstorage failed to set: ",r)}return Promise.resolve(!1)},e})(),BT="https://sr-client-cfg.amplitude.com/config",jT="https://sr-client-cfg.eu.amplitude.com/config",qT=3,ma={INVALID_API_KEY:401,FORBIDDEN:403,RATE_LIMIT:429},VT=1e3,$T=300*1e3,Xp=(function(){function e(t,r,n,i){n===void 0&&(n="US"),this.callbackInfos=[],this.lastSuccessfulFetch=null,this.fetchPromise=null,this.isLastFetchInvalidApiKey=!1,this.apiKey=t,this.serverUrl=i||(n==="US"?BT:jT),this.logger=r,this.storage=new HT(t,r)}return e.prototype.subscribe=function(t,r,n){var i=Qt(),o={id:i,key:t,deliveryMode:r,callback:n};return this.callbackInfos.push(o),r==="all"?this.subscribeAll(o):this.subscribeWaitForRemote(o,r.timeout),i},e.prototype.unsubscribe=function(t){var r=this.callbackInfos.findIndex(function(n){return n.id===t});return r===-1?(this.logger.debug("Remote config client unsubscribe failed because callback with id ".concat(t," doesn't exist.")),!1):(this.callbackInfos.splice(r,1),this.logger.debug("Remote config client unsubscribe succeeded removing callback with id ".concat(t,".")),!0)},e.prototype.updateConfigs=function(){return x(this,void 0,void 0,function(){var t,r,n=this;return O(this,function(i){switch(i.label){case 0:return this.lastSuccessfulFetch&&(t=Date.now()-this.lastSuccessfulFetch,t<$T)?(this.logger.debug("Remote config client skipping updateConfigs: Too recent"),[2]):[4,this.getOrCreateFetchPromise()];case 1:return r=i.sent(),this.storage.setConfig(r),this.callbackInfos.forEach(function(o){n.sendCallback(o,r,"remote")}),[2]}})})},e.prototype.getOrCreateFetchPromise=function(){var t=this;return this.fetchPromise?this.fetchPromise:this.isLastFetchInvalidApiKey?(this.logger.debug("Remote config client skipping fetch: Invalid API key"),this.fetchPromise=Promise.resolve({remoteConfig:null,lastFetch:new Date}).finally(function(){t.fetchPromise=null}),this.fetchPromise):(this.fetchPromise=this.fetch().then(function(r){return r.remoteConfig!==null&&(t.lastSuccessfulFetch=Date.now()),r}).finally(function(){t.fetchPromise=null}),this.fetchPromise)},e.prototype.subscribeAll=function(t){return x(this,void 0,void 0,function(){var r,n,i,o=this;return O(this,function(s){switch(s.label){case 0:return r=this.getOrCreateFetchPromise().then(function(a){o.logger.debug("Remote config client subscription all mode fetched from remote: ".concat(JSON.stringify(a))),o.sendCallback(t,a,"remote"),o.storage.setConfig(a)}),n=this.storage.fetchConfig().then(function(a){return a}),[4,Promise.race([r,n])];case 1:return i=s.sent(),i!==void 0&&(this.logger.debug("Remote config client subscription all mode fetched from cache: ".concat(JSON.stringify(i))),i.remoteConfig!==null?this.sendCallback(t,i,"cache"):this.logger.debug("Remote config client skips sending callback because cache is empty (first time user).")),[4,r];case 2:return s.sent(),[2]}})})},e.prototype.subscribeWaitForRemote=function(t,r){return x(this,void 0,void 0,function(){var n,i,i;return O(this,function(o){switch(o.label){case 0:n=new Promise(function(s,a){setTimeout(function(){a("Timeout exceeded")},r)}),o.label=1;case 1:return o.trys.push([1,3,,5]),[4,Promise.race([this.getOrCreateFetchPromise(),n])];case 2:return i=o.sent(),this.logger.debug("Remote config client subscription wait for remote mode returns from remote."),this.sendCallback(t,i,"remote"),this.storage.setConfig(i),[3,5];case 3:return o.sent(),this.logger.debug("Remote config client subscription wait for remote mode exceeded timeout. Try to fetch from cache."),[4,this.storage.fetchConfig()];case 4:return i=o.sent(),i.remoteConfig!==null?(this.logger.debug("Remote config client subscription wait for remote mode returns a cached copy."),this.sendCallback(t,i,"cache")):(this.logger.debug("Remote config client subscription wait for remote mode failed to fetch cache."),this.sendCallback(t,i,"remote")),[3,5];case 5:return[2]}})})},e.prototype.sendCallback=function(t,r,n){t.lastCallback=new Date;var i;t.key?i=t.key.split(".").reduce(function(o,s){return o===null?o:s in o?o[s]:null},r.remoteConfig):i=r.remoteConfig,t.callback(i,n,r.lastFetch)},e.prototype.fetch=function(t,r){return t===void 0&&(t=qT),r===void 0&&(r=VT),x(this,void 0,void 0,function(){var n,i,o,s,a,u,c=this;return O(this,function(l){switch(l.label){case 0:n=r/t,i={remoteConfig:null,lastFetch:new Date},o=function(f){var d,h,v,p,b,E,m;return O(this,function(g){switch(g.label){case 0:d=!0,h=new AbortController,v=setTimeout(function(){return h.abort()},r),g.label=1;case 1:return g.trys.push([1,7,8,9]),[4,fetch(s.getUrlParams(),{method:"GET",headers:{Accept:"*/*"},signal:h.signal})];case 2:return p=g.sent(),p.ok?[3,4]:[4,p.text()];case 3:return b=g.sent(),s.logger.debug("Remote config client fetch with retry time ".concat(t," failed with ").concat(p.status,": ").concat(b)),p.status===ma.INVALID_API_KEY||p.status===ma.FORBIDDEN?(s.logger.error("Remote config client fetch failed with ".concat(p.status,". Invalid API key; future fetches will be skipped.")),s.isLastFetchInvalidApiKey=!0,d=!1):p.status>=400&&p.status<500&&p.status!==ma.RATE_LIMIT&&(d=!1),[3,6];case 4:return[4,p.json()];case 5:return E=g.sent(),[2,{value:{remoteConfig:E,lastFetch:new Date}}];case 6:return[3,9];case 7:return m=g.sent(),m instanceof Error&&m.name==="AbortError"?s.logger.debug("Remote config client fetch with retry time ".concat(t," timed out after ").concat(r,"ms")):s.logger.debug("Remote config client fetch with retry time ".concat(t," is rejected because: "),m),[3,9];case 8:return clearTimeout(v),[7];case 9:return d?f=0;s--)if(o[s]===e){o.splice(s,1);break}}catch(a){t={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}}function zT(){var e,t;try{for(var r=ce(Object.entries(xi)),n=r.next();!n.done;n=r.next()){var i=oe(n.value,2),o=i[0],s=i[1];s&&(Sn[o]=s)}}catch(a){e={error:a}}finally{try{n&&!n.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}xi={},sn={}}var qf={addListener:GT,removeListener:KT,_restoreConsole:zT},Xn;(function(e){e.US="US",e.EU="EU",e.STAGING="STAGING"})(Xn||(Xn={}));var Yp=null,Jp=["a","button","input","select","textarea","label","video","audio",'[contenteditable="true" i]',"[data-amp-default-track]",".amp-default-track"],Cl="data-amp-track-",Qp=["div","span","h1","h2","h3","h4","h5","h6"],Zp=150,XT=["a","button",'[role="button"]','[role="link"]','[role="menuitem"]','[role="menuitemcheckbox"]','[role="menuitemradio"]','[role="option"]','[role="tab"]','[role="treeitem"]','[contenteditable="true" i]'],eg=Ee(['input[type="button"]','input[type="submit"]','input[type="reset"]','input[type="image"]','input[type="file"]'],oe(XT),!1),YT=eg,JT=eg,QT=["*"],ZT=1e3,eA=4,tA=50;function rA(e){return typeof e=="string"||typeof e=="number"||typeof e=="boolean"||e===null||e===void 0}function tg(e,t,r){if(e){var n=t.map(Vf),i=r.map(Vf);rg({json:e,allowlist:n,excludelist:i,ancestors:[]})}}function rg(e){var t,r,n=e.json,i=e.targetObject,o=e.allowlist,s=e.excludelist,a=e.ancestors,u=e.parentObject,c=e.targetKey;i||(i=n);var l=Object.keys(i);try{for(var f=ce(l),d=f.next();!d.done;d=f.next()){var h=d.value,v=Ee(Ee([],oe(a),!1),[h],!1);rA(i[h])?(!$f(v,o)||$f(v,s))&&delete i[h]:rg({json:n,targetObject:i[h],allowlist:o,excludelist:s,ancestors:v,parentObject:i,targetKey:h})}}catch(p){t={error:p}}finally{try{d&&!d.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}Object.keys(i).length===0&&u&&c&&delete u[c]}function Vf(e){return e.startsWith("/")&&(e=e.slice(1)),e.split("/").map(function(t){return t.replace(/~0/g,"~").replace(/~1/g,"/")})}function wu(e,t,r,n){if(r===void 0&&(r=0),n===void 0&&(n=0),n===t.length)return r===e.length;if(r===e.length){for(;n=t)return}}catch(b){r={error:b}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}i=c}else if(e instanceof ReadableStream){a=e;return}return i}}var sA=(function(){function e(t){this.response=t}return e.prototype.headers=function(t){var r;if(t===void 0&&(t=[]),this.response.headers instanceof Headers){var n=this.response.headers,i={};return(r=n?.forEach)===null||r===void 0||r.call(n,function(o,s){i[s]=o}),Bs(i,{allow:t})}},Object.defineProperty(e.prototype,"bodySize",{get:function(){var t,r;if(this._bodySize!==void 0)return this._bodySize;var n=(r=(t=this.response.headers)===null||t===void 0?void 0:t.get)===null||r===void 0?void 0:r.call(t,"content-length"),i=n?parseInt(n,10):void 0;return this._bodySize=i,i},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"status",{get:function(){return this.response.status},enumerable:!1,configurable:!0}),e.prototype.text=function(){return x(this,void 0,void 0,function(){var t,r,n;return O(this,function(i){switch(i.label){case 0:this.clonedResponse||(this.clonedResponse=this.response.clone()),i.label=1;case 1:return i.trys.push([1,3,,4]),t=this.clonedResponse.text(),r=new Promise(function(o){return setTimeout(function(){return o(null)},nA)}),[4,Promise.race([t,r])];case 2:return n=i.sent(),[2,n];case 3:return i.sent(),[2,null];case 4:return[2]}})})},e.prototype.json=function(t,r){return t===void 0&&(t=[]),r===void 0&&(r=[]),x(this,void 0,void 0,function(){var n;return O(this,function(i){switch(i.label){case 0:return t.length===0?[2,null]:[4,this.text()];case 1:return n=i.sent(),[2,kl(n,t,r)]}})})},e})(),aA=(function(){function e(t,r,n,i){this.statusCode=t,this.headersString=r,this.size=n,this.getJson=i}return Object.defineProperty(e.prototype,"bodySize",{get:function(){return this.size},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"status",{get:function(){return this.statusCode},enumerable:!1,configurable:!0}),e.prototype.headers=function(t){var r,n;if(t===void 0&&(t=[]),!this.headersString)return{};var i={},o=this.headersString.split(`\r -`);try{for(var s=ce(o),a=s.next();!a.done;a=s.next()){var u=a.value,c=oe(u.split(": "),2),l=c[0],f=c[1];l&&f&&(i[l]=f)}}catch(d){r={error:d}}finally{try{a&&!a.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}return Bs(i,{allow:t})},e.prototype.json=function(t,r){return t===void 0&&(t=[]),r===void 0&&(r=[]),x(this,void 0,void 0,function(){var n;return O(this,function(i){return t.length===0?[2,null]:(n=this.getJson(),n?(tg(n,t,r),[2,n]):[2,null])})})},e})();function kl(e,t,r){if(!e)return null;try{var n=JSON.parse(e);return tg(n,t,r),n}catch{return null}}var Ni;(function(e){e.REDACT="redact",e.REMOVE="remove"})(Ni||(Ni={}));var Wf="[REDACTED]",Bs=function(e,t){var r,n,i=t.allow,o=i===void 0?[]:i,s=t.strategy,a=s===void 0?Ni.REMOVE:s,u=Ee([],oe(Hw),!1),c={},l=function(v){var p=v.toLowerCase();u.find(function(b){return b.toLowerCase()===p})?a===Ni.REDACT&&(c[v]=Wf):o.find(function(b){return b.toLowerCase()===p})?c[v]=e[v]:a===Ni.REDACT&&(c[v]=Wf)};try{for(var f=ce(Object.keys(e)),d=f.next();!d.done;d=f.next()){var h=d.value;l(h)}}catch(v){r={error:v}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}return c},uA=(function(){function e(t,r,n,i,o,s,a,u,c,l,f){a===void 0&&(a=0),this.type=t,this.method=r,this.timestamp=n,this.startTime=i,this.url=o,this.requestWrapper=s,this.status=a,this.duration=u,this.responseWrapper=c,this.error=l,this.endTime=f}return e.prototype.toSerializable=function(){var t,r,n,i,o={type:this.type,method:this.method,url:this.url,timestamp:this.timestamp,status:this.status,duration:this.duration,error:this.error,startTime:this.startTime,endTime:this.endTime,requestHeaders:(t=this.requestWrapper)===null||t===void 0?void 0:t.headers(Ee([],oe(vs),!1)),requestBodySize:(r=this.requestWrapper)===null||r===void 0?void 0:r.bodySize,responseHeaders:(n=this.responseWrapper)===null||n===void 0?void 0:n.headers(Ee([],oe(vs),!1)),responseBodySize:(i=this.responseWrapper)===null||i===void 0?void 0:i.bodySize};return Object.fromEntries(Object.entries(o).filter(function(s){var a=oe(s,2);a[0];var u=a[1];return u!==void 0}))},e})();function lA(e){return typeof e=="object"&&e!==null&&"url"in e&&"method"in e}var cA=(function(){function e(t,r){r===void 0&&(r=Qt()),this.callback=t,this.id=r}return e})();function $r(e){try{e()}catch{}}var fA=(function(){function e(t){this.eventCallbacks=new Map,this.isObserving=!1,this.logger=t;var r=le();e.isSupported()&&(this.globalScope=r)}return e.isSupported=function(){var t=le();return!!t&&!!t.fetch},e.prototype.subscribe=function(t,r){var n,i,o,s,a,u,c,l,f,d;if(this.logger||(this.logger=r),this.eventCallbacks.set(t.id,t),!this.isObserving){var h=(o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.XMLHttpRequest)===null||i===void 0?void 0:i.prototype)===null||o===void 0?void 0:o.open,v=(u=(a=(s=this.globalScope)===null||s===void 0?void 0:s.XMLHttpRequest)===null||a===void 0?void 0:a.prototype)===null||u===void 0?void 0:u.send,p=(f=(l=(c=this.globalScope)===null||c===void 0?void 0:c.XMLHttpRequest)===null||l===void 0?void 0:l.prototype)===null||f===void 0?void 0:f.setRequestHeader;h&&v&&p&&this.observeXhr(h,v,p);var b=(d=this.globalScope)===null||d===void 0?void 0:d.fetch;b&&this.observeFetch(b),this.isObserving=!0}},e.prototype.unsubscribe=function(t){this.eventCallbacks.delete(t.id)},e.prototype.triggerEventCallbacks=function(t){var r=this;this.eventCallbacks.forEach(function(n){try{n.callback(t)}catch(i){$r(function(){var o;(o=r.logger)===null||o===void 0||o.debug("an unexpected error occurred while triggering event callbacks",i)})}})},e.prototype.handleNetworkRequestEvent=function(t,r,n,i,o,s,a){var u;if(!(s===void 0||a===void 0)){var c,l="GET";if(lA(r)?(c=r.url,l=r.method):c=(u=r?.toString)===null||u===void 0?void 0:u.call(r),c)try{var f=new URL(c);c="".concat(f.protocol,"//").concat(f.host).concat(f.pathname).concat(f.search).concat(f.hash)}catch{}l=n?.method||l;var d,h;i&&(d=i.status),o&&(h={name:o.name||"UnknownError",message:o.message||"An unknown error occurred"},d=0);var v=Math.floor(performance.now()-a),p=Math.floor(s+v),b=new uA(t,l,s,s,c,n,d,v,i,h,p);this.triggerEventCallbacks(b)}},e.prototype.getTimestamps=function(){var t,r;return{startTime:(t=Date.now)===null||t===void 0?void 0:t.call(Date),durationStart:(r=performance?.now)===null||r===void 0?void 0:r.call(performance)}},e.prototype.observeFetch=function(t){var r=this;!this.globalScope||!t||(this.globalScope.fetch=function(n,i){return x(r,void 0,void 0,function(){var o,s,a,u,c=this;return O(this,function(l){switch(l.label){case 0:try{o=this.getTimestamps()}catch(f){$r(function(){var d;return(d=c.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while retrieving timestamps",f)})}l.label=1;case 1:return l.trys.push([1,3,,4]),[4,t(n,i)];case 2:return s=l.sent(),[3,4];case 3:return u=l.sent(),a=u,[3,4];case 4:try{this.handleNetworkRequestEvent("fetch",n,i?new iA(i):void 0,s?new sA(s):void 0,a,o?.startTime,o?.durationStart)}catch(f){$r(function(){var d;return(d=c.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while handling fetch",f)})}if(s)return[2,s];throw a}})})})},e.createXhrJsonParser=function(t,r){return function(){var n;try{if(t.responseType==="json"){if(!((n=r.globalScope)===null||n===void 0)&&n.structuredClone)return r.globalScope.structuredClone(t.response)}else if(["text",""].includes(t.responseType))return JSON.parse(t.responseText)}catch(i){return i instanceof Error&&i.name==="InvalidStateError"&&$r(function(){var o;return(o=r.logger)===null||o===void 0?void 0:o.debug("unexpected error when retrieving responseText. responseType='".concat(t.responseType,"'"))}),null}return null}},e.prototype.observeXhr=function(t,r,n){if(!(!this.globalScope||!t||!r)){var i=this.globalScope.XMLHttpRequest.prototype,o=this;i.open=function(){for(var s,a=[],u=0;u"u"||!document.title)return"";var t=document.querySelector("title");return t&&t.hasAttribute(Tn)?Nr:e?e(document.title):document.title};function sg(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ci={},Kf;function pA(){if(Kf)return ci;Kf=1,Object.defineProperty(ci,"__esModule",{value:!0}),ci.Observable=void 0;const e=E=>!!Symbol[E],t=E=>e(E)?Symbol[E]:"@@"+E,r=t("iterator"),n=t("observable"),i=t("species");function o(E,m){let g=E[m];if(g!=null){if(typeof g!="function")throw new TypeError(g+" is not a function");return g}}function s(E){let m=E.constructor;return m!==void 0&&(m=m[i],m===null&&(m=void 0)),m!==void 0?m:b}function a(E){return E instanceof b}function u(E){u.log?u.log(E):setTimeout(()=>{throw E})}function c(E){Promise.resolve().then(()=>{try{E()}catch(m){u(m)}})}function l(E){let m=E._cleanup;if(m!==void 0&&(E._cleanup=void 0,!!m))try{if(typeof m=="function")m();else{let g=o(m,"unsubscribe");g&&g.call(m)}}catch(g){u(g)}}function f(E){E._observer=void 0,E._queue=void 0,E._state="closed"}function d(E){let m=E._queue;if(m){E._queue=void 0,E._state="ready";for(let g=0;gd(E));return}h(E,m,g)}}class p{constructor(m,g){this._cleanup=void 0,this._observer=m,this._queue=void 0,this._state="initializing";let y=this,_={get closed(){return y._state==="closed"},next(S){v(y,"next",S)},error(S){v(y,"error",S)},complete(){v(y,"complete")}};try{this._cleanup=g.call(void 0,_)}catch(S){_.error(S)}this._state==="initializing"&&(this._state="ready")}get closed(){return this._state==="closed"}unsubscribe(){this._state!=="closed"&&(f(this),l(this))}}class b{constructor(m){if(!(this instanceof b))throw new TypeError("Observable cannot be called as a function");if(typeof m!="function")throw new TypeError("Observable initializer must be a function");this._subscriber=m}subscribe(m){return(typeof m!="object"||m===null)&&(m={next:m,error:arguments[1],complete:arguments[2]}),new p(m,this._subscriber)}forEach(m){return new Promise((g,y)=>{if(typeof m!="function"){y(new TypeError(m+" is not a function"));return}function _(){S.unsubscribe(),g()}let S=this.subscribe({next(A){try{m(A,_)}catch(I){y(I),S.unsubscribe()}},error:y,complete:g})})}map(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>this.subscribe({next(_){try{_=m(_)}catch(S){return y.error(S)}y.next(_)},error(_){y.error(_)},complete(){y.complete()}}))}filter(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>this.subscribe({next(_){try{if(!m(_))return}catch(S){return y.error(S)}y.next(_)},error(_){y.error(_)},complete(){y.complete()}}))}reduce(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this),y=arguments.length>1,_=!1,A=arguments[1];return new g(I=>this.subscribe({next(C){let R=!_;if(_=!0,!R||y)try{A=m(A,C)}catch(P){return I.error(P)}else A=C},error(C){I.error(C)},complete(){if(!_&&!y)return I.error(new TypeError("Cannot reduce an empty sequence"));I.next(A),I.complete()}}))}async all(){let m=[];return await this.forEach(g=>m.push(g)),m}concat(...m){let g=s(this);return new g(y=>{let _,S=0;function A(I){_=I.subscribe({next(C){y.next(C)},error(C){y.error(C)},complete(){S===m.length?(_=void 0,y.complete()):A(g.from(m[S++]))}})}return A(this),()=>{_&&(_.unsubscribe(),_=void 0)}})}flatMap(m){if(typeof m!="function")throw new TypeError(m+" is not a function");let g=s(this);return new g(y=>{let _=[],S=this.subscribe({next(I){if(m)try{I=m(I)}catch(R){return y.error(R)}let C=g.from(I).subscribe({next(R){y.next(R)},error(R){y.error(R)},complete(){let R=_.indexOf(C);R>=0&&_.splice(R,1),A()}});_.push(C)},error(I){y.error(I)},complete(){A()}});function A(){S.closed&&_.length===0&&y.complete()}return()=>{_.forEach(I=>I.unsubscribe()),S.unsubscribe()}})}[n](){return this}static from(m){let g=typeof this=="function"?this:b;if(m==null)throw new TypeError(m+" is not an object");let y=o(m,n);if(y){let _=y.call(m);if(Object(_)!==_)throw new TypeError(_+" is not an object");return a(_)&&_.constructor===g?_:new g(S=>_.subscribe(S))}if(e("iterator")&&(y=o(m,r),y))return new g(_=>{c(()=>{if(!_.closed){for(let S of y.call(m))if(_.next(S),_.closed)return;_.complete()}})});if(Array.isArray(m))return new g(_=>{c(()=>{if(!_.closed){for(let S=0;S{c(()=>{if(!y.closed){for(let _=0;_0&&setTimeout(function(){c(new Error("".concat(t," timed out (id: ").concat(o,")"))),delete i.requestCallbacks[o]},n.timeout)});return a},e.prototype.handleResponse=function(t){var r;if(!this.requestCallbacks[t.id]){(r=this.logger)===null||r===void 0||r.warn("No callback found for request id: ".concat(t.id));return}this.requestCallbacks[t.id].resolve(t.responseData),delete this.requestCallbacks[t.id]},e.prototype.registerActionHandler=function(t,r){var n,i,o,s;this.actionHandlers.has(t)&&((s=(o=this.logger)===null||o===void 0?void 0:o.warn)===null||s===void 0||s.call(o,"Overwriting existing action handler for: ".concat(t))),this.actionHandlers.set(t,r);var a=this.pendingMessages.get(t);if(a){this.pendingMessages.delete(t);try{for(var u=ce(a),c=u.next();!c.done;c=u.next()){var l=c.value;r(l)}}catch(f){n={error:f}}finally{try{c&&!c.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}}},e.prototype.loadScriptOnce=function(t){return x(this,void 0,void 0,function(){var r,n,i;return O(this,function(o){switch(o.label){case 0:if(r=this.scriptLoadPromises.get(t),r)return[2,r];n=TA(t).then(function(){}),this.scriptLoadPromises.set(t,n),o.label=1;case 1:return o.trys.push([1,3,,4]),[4,n];case 2:return o.sent(),[3,4];case 3:throw i=o.sent(),this.scriptLoadPromises.delete(t),i;case 4:return[2]}})})},e.prototype.setup=function(t){var r=this,n,i,o=t===void 0?{}:t,s=o.logger,a=o.endpoint;s&&(this.logger=s),a&&this.endpoint===Tu&&(this.endpoint=a),!this.isSetup&&(this.isSetup=!0,(i=(n=this.logger)===null||n===void 0?void 0:n.debug)===null||i===void 0||i.call(n,"Setting up messenger"),this.messageHandler=function(u){var c,l,f,d,h;if((l=(c=r.logger)===null||c===void 0?void 0:c.debug)===null||l===void 0||l.call(c,"Message received: ",JSON.stringify(u)),r.endpoint===u.origin){var v=u.data,p=v?.action;if(p)if("id"in v&&v.id)(d=(f=r.logger)===null||f===void 0?void 0:f.debug)===null||d===void 0||d.call(f,"Received Response to previous request: ",JSON.stringify(u)),r.handleResponse(v);else{if(p==="ping"){r.notify({action:"pong"});return}var b=r.actionHandlers.get(p);if(b)b(v.data);else{var E=(h=r.pendingMessages.get(p))!==null&&h!==void 0?h:[];E.push(v.data),r.pendingMessages.set(p,E)}}}},window.addEventListener("message",this.messageHandler),this.notify({action:"page-loaded"}))},e.prototype.destroy=function(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.isSetup=!1,this.actionHandlers.clear(),this.pendingMessages.clear(),this.requestCallbacks={},this.scriptLoadPromises.clear();var t=le();t?.[ms]===this&&delete t[ms]},e})();cg=Au;function CA(e){return typeof e=="object"&&e!==null&&Au in e&&e[Au]===!0}function fg(e){var t=le(),r=t?.[ms];if(CA(r))return r;var n=new IA(e);return t&&(t[ms]=n),n}var Yf="__AMPLITUDE_BACKGROUND_CAPTURE__";function dg(e,t){var r,n=e;if(n[Yf]!==!0){n[Yf]=!0;var i=(r=void 0)!==null&&r!==void 0?r:wA,o=null,s=function(a,u){var c,l;a==="background-capture-complete"&&((l=(c=e.logger)===null||c===void 0?void 0:c.debug)===null||l===void 0||l.call(c,"Background capture complete"),e.notify({action:"background-capture-complete",data:u}))};e.registerActionHandler("initialize-background-capture",function(){var a,u;(u=(a=e.logger)===null||a===void 0?void 0:a.debug)===null||u===void 0||u.call(a,"Initializing background capture (external script)");var c=new URL(i,e.endpoint).toString();e.loadScriptOnce(c).then(function(){var l,f,d;(f=(l=e.logger)===null||l===void 0?void 0:l.debug)===null||f===void 0||f.call(l,"Background capture script loaded (external)"),o=(d=window?.amplitudeBackgroundCapture)===null||d===void 0?void 0:d.call(window,{messenger:e,onBackgroundCapture:s}),e.notify({action:"background-capture-loaded"})}).catch(function(){var l;(l=e.logger)===null||l===void 0||l.warn("Failed to initialize background capture")})}),e.registerActionHandler("close-background-capture",function(){var a;(a=o?.close)===null||a===void 0||a.call(o),o=null})}}var jo={always:"always",ifEmptyCampaign:"ifEmptyCampaign"},ei=function(e,t){return typeof e=="boolean"?e:e?.[t]!==!1},hg=function(e){return ei(e,"attribution")},kA=function(e){return ei(e,"fileDownloads")},vg=function(e){return ei(e,"formInteractions")},pg=function(e){return ei(e,"pageViews")},Jf=function(e){return ei(e,"sessions")},RA=function(e){return ei(e,"pageUrlEnrichment")},gg=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.networkTracking===!0||typeof e.networkTracking=="object")},mg=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.elementInteractions===!0||typeof e.elementInteractions=="object")},PA=function(e){return typeof e=="boolean"?e:typeof e=="object"&&e.webVitals===!0},yg=function(e){return typeof e=="boolean"?e:typeof e=="object"&&(e.frustrationInteractions===!0||typeof e.frustrationInteractions=="object")},OA=function(e){return typeof e=="boolean"?e:typeof e=="object"&&e!==null&&e.enabled!==!1},xA=function(e){if(mg(e.autocapture)&&typeof e.autocapture=="object"&&typeof e.autocapture.elementInteractions=="object")return e.autocapture.elementInteractions},NA=function(e){if(yg(e.autocapture)&&typeof e.autocapture=="object"&&typeof e.autocapture.frustrationInteractions=="object")return e.autocapture.frustrationInteractions},LA=function(e){var t;if(gg(e.autocapture)){var r=void 0;return typeof e.autocapture=="object"&&typeof e.autocapture.networkTracking=="object"?r=e.autocapture.networkTracking:e.networkTrackingOptions&&(r=e.networkTrackingOptions),D(D({},r),{captureRules:(t=r?.captureRules)===null||t===void 0?void 0:t.map(function(n){var i,o,s;if(!((i=n.urls)===null||i===void 0)&&i.length&&(!((o=n.hosts)===null||o===void 0)&&o.length)){var a=JSON.stringify(n.hosts),u=JSON.stringify(n.urls);return(s=e.loggerProvider)===null||s===void 0||s.warn("Found network capture rule with both urls='".concat(u,"' and hosts='").concat(a,"' set. ")+"Definition of urls takes precedence over hosts, so ignoring hosts."),D(D({},n),{hosts:void 0})}return n})})}},Qf=function(e){var t=function(){return!1},r=void 0,n,i=e.pageCounter,o=pg(e.defaultTracking);return o&&(t=void 0,n=void 0,e.defaultTracking&&typeof e.defaultTracking=="object"&&e.defaultTracking.pageViews&&typeof e.defaultTracking.pageViews=="object"&&("trackOn"in e.defaultTracking.pageViews&&(t=e.defaultTracking.pageViews.trackOn),"trackHistoryChanges"in e.defaultTracking.pageViews&&(r=e.defaultTracking.pageViews.trackHistoryChanges),"eventType"in e.defaultTracking.pageViews&&e.defaultTracking.pageViews.eventType&&(n=e.defaultTracking.pageViews.eventType))),{trackOn:t,trackHistoryChanges:r,eventType:n,pageCounter:i}},Zf=function(e){return hg(e.defaultTracking)&&e.defaultTracking&&typeof e.defaultTracking=="object"&&e.defaultTracking.attribution&&typeof e.defaultTracking.attribution=="object"?D({},e.defaultTracking.attribution):{}},MA=function(e){if(vg(e.defaultTracking)&&e.defaultTracking&&typeof e.defaultTracking=="object"&&typeof e.defaultTracking.formInteractions=="object")return e.defaultTracking.formInteractions},_a=function(e,t){for(var r=0;r"u"&&u(new Error("XHRTransport is not supported."));var c=new XMLHttpRequest;c.open("POST",r,!0),c.onreadystatechange=function(){if(c.readyState===o.state.done){var p=c.responseText;try{a(o.buildResponse(JSON.parse(p)))}catch{a(o.buildResponse({code:c.status}))}}};var l={"Content-Type":"application/json",Accept:"*/*"},f=JSON.stringify(n),d=i&&f.length>=Gp&&Kp(),h=function(p){var b,E;l=D(D({},o.customHeaders),l);try{for(var m=ce(Object.entries(l)),g=m.next();!g.done;g=m.next()){var y=oe(g.value,2),_=y[0],S=y[1];c.setRequestHeader(_,S)}}catch(A){b={error:A}}finally{try{g&&!g.done&&(E=m.return)&&E.call(m)}finally{if(b)throw b.error}}c.send(p)},v=function(){return x(o,void 0,void 0,function(){var p;return O(this,function(b){switch(b.label){case 0:return d?[4,zp(f)]:[3,2];case 1:return p=b.sent(),p?(l["Content-Encoding"]="gzip",h(p)):h(f),[3,3];case 2:h(f),b.label=3;case 3:return[2]}})})};v().catch(u)})]})})},t})(ro),jA=(function(e){Mt(t,e);function t(r){r===void 0&&(r={});var n=e.call(this)||this;return n.customHeaders=r,n}return t.prototype.send=function(r,n,i){return i===void 0&&(i=!1),x(this,void 0,void 0,function(){var o,s,a,u,c,l,f,d;return O(this,function(h){switch(h.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return o=JSON.stringify(n),s=i&&o.length>=Gp&&Kp(),a=o,u={"Content-Type":"application/json",Accept:"*/*"},s?[4,zp(o)]:[3,2];case 1:c=h.sent(),c&&(u["Content-Encoding"]="gzip",a=c),h.label=2;case 2:return u=D(D({},this.customHeaders),u),l={headers:u,body:a,method:"POST"},[4,fetch(r,l)];case 3:return f=h.sent(),[4,f.text()];case 4:d=h.sent();try{return[2,this.buildResponse(JSON.parse(d))]}catch{return[2,this.buildResponse({code:f.status})]}return[2]}})})},t})(ro),qA=(function(e){Mt(t,e);function t(){return e.call(this)||this}return t.prototype.send=function(r,n,i){return x(this,void 0,void 0,function(){var o=this;return O(this,function(s){return[2,new Promise(function(a,u){var c=le();if(!c?.navigator.sendBeacon)throw new Error("SendBeaconTransport is not supported");try{var l=JSON.stringify(n),f=c.navigator.sendBeacon(r,l);return a(f?o.buildResponse({code:200,events_ingested:n.events.length,payload_size_bytes:l.length,server_upload_time:Date.now()}):o.buildResponse({code:500}))}catch(d){u(d)}})]})})},t})(ro),VA=function(e,t,r){return r===void 0&&(r=!0),x(void 0,void 0,void 0,function(){var n,i,o,s,a,u,c,l,f;return O(this,function(d){switch(d.label){case 0:return n=ET(e),[4,t.getRaw(n)];case 1:return i=d.sent(),i?r?[4,t.remove(n)]:[3,3]:[2,{optOut:!1}];case 2:d.sent(),d.label=3;case 3:return o=oe(i.split("."),6),s=o[0],a=o[1],u=o[2],c=o[3],l=o[4],f=o[5],[2,{deviceId:s,userId:$A(a),sessionId:wa(c),lastEventId:wa(f),lastEventTime:wa(l),optOut:!!u}]}})})},wa=function(e){var t=parseInt(e,32);if(!isNaN(t))return t},$A=function(e){if(!(!atob||!escape||!e))try{return decodeURIComponent(escape(atob(e)))}catch{return}},$t="[Amplitude]",ed="".concat($t," Form Started"),WA="".concat($t," Form Submitted"),GA="".concat($t," File Downloaded"),td="session_start",rd="session_end",KA="".concat($t," File Extension"),zA="".concat($t," File Name"),XA="".concat($t," Link ID"),YA="".concat($t," Link Text"),JA="".concat($t," Link URL"),Ta="".concat($t," Form ID"),Aa="".concat($t," Form Name"),Ia="".concat($t," Form Destination"),ys="cookie",_g="US";const QA=Object.freeze(Object.defineProperty({__proto__:null,DEFAULT_ACTION_CLICK_ALLOWLIST:Qp,DEFAULT_CSS_SELECTOR_ALLOWLIST:Jp,DEFAULT_DATA_ATTRIBUTE_PREFIX:Cl,EXCLUDE_INTERNAL_REFERRERS_CONDITIONS:jo,get IdentifyOperation(){return hr},get LogLevel(){return ot},OfflineDisabled:Yp,get RevenueProperty(){return Ut},get ServerZone(){return Xn},get SpecialEventType(){return _t}},Symbol.toStringTag,{value:"Module"}));var nd=function(e){var t=e.split(".");return t.length<=2?e:t.slice(t.length-2,t.length).join(".")},ZA=function(e){return Object.values(e).every(function(t){return!t})},eI=function(e){var t=D(D({},e),{referring_domain:void 0,referrer:void 0});return Object.values(t).every(function(r){return!r})},tI=function(e,t,r,n,i,o){i===void 0&&(i=!0),e.referrer;var s=e.referring_domain,a=hs(e,["referrer","referring_domain"]),u=t||{};u.referrer;var c=u.referring_domain,l=hs(u,["referrer","referring_domain"]),f=r.excludeInternalReferrers;if(f){var d=sI(f,n);if(!(d instanceof TypeError)&&e.referring_domain&&aI(e.referring_domain,o)){if(d==="always")return id(d,e.referring_domain,n),!1;if(d==="ifEmptyCampaign"&&eI(e))return id(d,e.referring_domain,n),!1}}if(rI(r.excludeReferrers,e.referring_domain))return n.debug("This is not a new campaign because ".concat(e.referring_domain," is in the exclude referrer list.")),!1;if(!i&&ZA(e)&&t)return n.debug("This is not a new campaign because this is a direct traffic in the same session."),!1;var h=JSON.stringify(a)!==JSON.stringify(l),v=nd(s||"")!==nd(c||""),p=!t||h||v;return p?n.debug("This is a new campaign. An $identify event will be sent."):n.debug("This is not a new campaign because it's the same as the previous one."),p},rI=function(e,t){return e===void 0&&(e=[]),t===void 0&&(t=""),e.some(function(r){return r instanceof RegExp?r.test(t):r===t})},nI=function(e,t){var r=t.startsWith(".")?t:".".concat(t),n=e.startsWith(".")?e:".".concat(e);return!!n.endsWith(r)},iI=function(e,t){var r=D(D({},Fs),e),n=Object.entries(r).reduce(function(i,o){var s,a=oe(o,2),u=a[0],c=a[1];return i.setOnce("initial_".concat(u),(s=c??t.initialEmptyValue)!==null&&s!==void 0?s:"EMPTY"),c?i.set(u,c):i.unset(u)},new Dn);return Tl(n)},oI=function(e){var t=e;return t?(t.startsWith(".")&&(t=t.substring(1)),[new RegExp("".concat(t.replace(".","\\."),"$"))]):[]},sI=function(e,t){if(e===!0)return jo.always;if(typeof e=="object"){var r=e.condition;if(typeof r=="string"&&Object.keys(jo).includes(r))return r;if(typeof r>"u")return jo.always}var n="Invalid configuration provided for attribution.excludeInternalReferrers: ".concat(JSON.stringify(e));return t.error(n),new TypeError(n)};function id(e,t,r){var n="This is not a new campaign because referring_domain=".concat(t," is on the same domain as the current page and it is configured to exclude internal referrers");e==="always"?r.debug(n):e==="ifEmptyCampaign"&&r.debug("".concat(n," with empty campaign parameters"))}var Sg=["ac.in","ac.jp","ac.kr","ac.th","ac.uk","ac.za","appspot.com","asn.au","azurewebsites.net","cloudfront.net","myshopify.com","blogspot.com","co.ca","co.in","co.jp","co.kr","co.nz","co.th","co.uk","co.za","com.ar","com.au","com.br","com.cn","com.hk","com.in","com.jp","com.kr","com.mx","com.pl","com.sg","com.tr","com.tw","ed.jp","edu.au","edu.br","edu.cn","edu.hk","edu.sg","edu.th","edu.tr","edu.tw","firebaseapp.com","fly.dev","gc.ca","geek.nz","github.io","gitlab.io","go.jp","go.kr","go.th","gob.ar","gob.mx","gov.au","gov.br","gov.cn","gov.hk","gov.in","gov.pl","gov.sg","gov.tr","gov.tw","gov.uk","gov.za","govt.nz","gr.jp","herokuapp.com","id.au","idv.hk","iwi.nz","lg.jp","ltd.uk","maori.nz","me.uk","mil.kr","ne.jp","ne.kr","net.au","net.br","net.cn","net.hk","net.in","net.nz","net.pl","net.sg","net.tr","net.tw","net.za","onrender.com","or.jp","or.kr","or.th","org.ar","org.au","org.br","org.cn","org.hk","org.in","org.mx","org.nz","org.pl","org.sg","org.tw","org.uk","org.za","pages.dev","pe.kr","plc.uk","re.kr","res.in","sch.uk","vercel.app","netlify.app","workers.dev"],wg=function(e){var t,r,n=e||((r=(t=le())===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.hostname);if(!n)return"";var i=n.split("."),o=i[i.length-1],s=i[i.length-2];return Sg.find(function(a){return n.endsWith(".".concat(a))})&&(o=i[i.length-2]+"."+i[i.length-1],s=i[i.length-3]),s?"".concat(s,".").concat(o):o},aI=function(e,t){var r=le();if(!r)return!1;var n=(t||"").trim()||wg(r.location.hostname);return nI(e,n)},uI=(function(e){Mt(t,e);function t(r,n,i,o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A,I,C,R,P,k,N,$,ee,B,z,K,Q,ae,fe,pe,ye,be,de,me,M,Z,X,re){i===void 0&&(i=new Il),o===void 0&&(o={domain:"",expiration:365,sameSite:"Lax",secure:!1,upgrade:!0}),c===void 0&&(c=1e3),l===void 0&&(l=5),f===void 0&&(f=30),d===void 0&&(d=ys),E===void 0&&(E=new Kn),m===void 0&&(m=ot.Warn),y===void 0&&(y=!1),_===void 0&&(_=!1),I===void 0&&(I=""),C===void 0&&(C=_g),k===void 0&&(k=1800*1e3),N===void 0&&(N=new Eg({loggerProvider:E})),$===void 0&&($={ipAddress:!0,language:!0,platform:!0}),ee===void 0&&(ee="fetch"),B===void 0&&(B=!1),z===void 0&&(z=!0),ye===void 0&&(ye=!0),be===void 0&&(be=0),Z===void 0&&(Z=!1),X===void 0&&(X=!1);var F=this,ge;F=e.call(this,{apiKey:r,storageProvider:N,transportProvider:Tg(ee)})||this,F.apiKey=r,F.appVersion=n,F.cookieOptions=o,F.defaultTracking=s,F.autocapture=a,F.flushIntervalMillis=c,F.flushMaxRetries=l,F.flushQueueSize=f,F.identityStorage=d,F.ingestionMetadata=h,F.instanceName=v,F.loggerProvider=E,F.logLevel=m,F.minIdLength=g,F.offline=y,F.partnerId=S,F.plan=A,F.serverUrl=I,F.serverZone=C,F.sessionTimeout=k,F.storageProvider=N,F.trackingOptions=$,F.transport=ee,F.useBatch=B,F.fetchRemoteConfig=z,F.networkTrackingOptions=fe,F.identify=pe,F.enableDiagnostics=ye,F.diagnosticsSampleRate=be,F.diagnosticsClient=de,F.remoteConfig=me,F.topLevelDomain=M,F.enableRequestBodyCompression=Z,F._enableRequestBodyCompressionExperimental=X,F.customEnrichment=re,F.version=xl,F._optOut=!1,F._cookieStorage=i,F.deviceId=u,F.lastEventId=p,F.lastEventTime=b,F.optOut=_,F.deferredSessionId=P,F.sessionId=R,F.pageCounter=Q,F.userId=K,F.debugLogsEnabled=ae,F.loggerProvider.enable(ae?ot.Debug:F.logLevel),F.networkTrackingOptions=fe,F.identify=pe,F.enableDiagnostics=ye,F.diagnosticsSampleRate=be,F.diagnosticsClient=de;var w=(ge=me?.fetchRemoteConfig)!==null&&ge!==void 0?ge:z;return F.remoteConfig=F.remoteConfig||{},F.remoteConfig.fetchRemoteConfig=w,F.fetchRemoteConfig=w,F.topLevelDomain=M||wg(),F}return Object.defineProperty(t.prototype,"cookieStorage",{get:function(){return this._cookieStorage},set:function(r){this._cookieStorage!==r&&(this._cookieStorage=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"deviceId",{get:function(){return this._deviceId},set:function(r){this._deviceId!==r&&(this._deviceId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"userId",{get:function(){return this._userId},set:function(r){this._userId!==r&&(this._userId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"sessionId",{get:function(){return this._sessionId},set:function(r){this._sessionId!==r&&(this._sessionId=r,r!==void 0&&this._deferredSessionId!==void 0&&(this._deferredSessionId=void 0),this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"deferredSessionId",{get:function(){return this._deferredSessionId},set:function(r){this._deferredSessionId!==r&&r!==this.sessionId&&(this._deferredSessionId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"optOut",{get:function(){return this._optOut},set:function(r){this._optOut!==r&&(this._optOut=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"lastEventTime",{get:function(){return this._lastEventTime},set:function(r){this._lastEventTime!==r&&(this._lastEventTime=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"lastEventId",{get:function(){return this._lastEventId},set:function(r){this._lastEventId!==r&&(this._lastEventId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"pageCounter",{get:function(){return this._pageCounter},set:function(r){this._pageCounter!==r&&(this._pageCounter=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"debugLogsEnabled",{set:function(r){this._debugLogsEnabled!==r&&(this._debugLogsEnabled=r,this.updateStorage())},enumerable:!1,configurable:!0}),t.prototype.updateStorage=function(){var r={deviceId:this._deviceId,userId:this._userId,sessionId:this._sessionId,deferredSessionId:this._deferredSessionId,optOut:this._optOut,lastEventTime:this._lastEventTime,lastEventId:this._lastEventId,pageCounter:this._pageCounter,debugLogsEnabled:this._debugLogsEnabled,cookieDomain:void 0};this.cookieStorage instanceof gs&&(r.cookieDomain=this.cookieStorage.options.domain),this.cookieStorage.set(Vp(this.apiKey),r)},t})(Bp),lI=function(e,t,r,n,i){return t===void 0&&(t={}),x(void 0,void 0,void 0,function(){var o,s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A,I,C,R,P,k,N,$,ee,B,z,K,Q,ae,fe,pe,ye,be,de,me,M,Z,X,re,F,ge,w,T,L,H,U,q,te;return O(this,function(G){switch(G.label){case 0:return o=t.identityStorage||ys,s="",o===ys&&!(!((R=t.cookieOptions)===null||R===void 0)&&R.domain)&&((P=t.cookieOptions)===null||P===void 0?void 0:P.domain)!==""?[4,dI(void 0,n)]:[3,2];case 1:s=G.sent(),G.label=2;case 2:return a=D({domain:(N=(k=t.cookieOptions)===null||k===void 0?void 0:k.domain)!==null&&N!==void 0?N:s,expiration:365,sameSite:"Lax",secure:!1,upgrade:!0},t.cookieOptions),u={duplicateResolverFn:function(Y){var V=$p(Y);if(!V)return!1;var W=JSON.parse(V);return Wp(W.cookieDomain,a.domain)},diagnosticsClient:n},c=cI(t.identityStorage,a,u),[4,VA(e,c,(ee=($=t.cookieOptions)===null||$===void 0?void 0:$.upgrade)!==null&&ee!==void 0?ee:!0)];case 3:return l=G.sent(),[4,c.get(Vp(e))];case 4:return f=G.sent(),d=ps(),h=d.ampTimestamp?Number(d.ampTimestamp):void 0,v=h?Date.now()"u"||!location.hostname)return[2,""];if(r=location.hostname,n=r.split("."),n.length===1)return[2,""];for(i=[],o=1,Sg.find(function(f){return r.endsWith(".".concat(f))})&&(o=2),s=n.length-o-1;s>=0;--s)i.push(n.slice(s).join("."));s=0,l.label=2;case 2:if(!(s"u"||typeof a=="function"&&a()},v=typeof location<"u"?location.href:null,p=function(){return x(void 0,void 0,void 0,function(){var m,g,y,_,S;return O(this,function(A){switch(A.label){case 0:return m=location.href,g=mI(u,m,v||"")&&h(),v=m,g?(y=void 0,s&&(y=Qt(),s.set(Ca,{pageViewId:y})),n?.log("Tracking page view event"),t!=null?[3,1]:[3,3]):[3,4];case 1:return S=(_=t).track,[4,d(y)];case 2:S.apply(_,[A.sent()]),A.label=3;case 3:A.label=4;case 4:return[2]}})})},b=function(){p()},E={name:"@amplitude/plugin-page-view-tracking-browser",type:"enrichment",setup:function(m,g){return x(void 0,void 0,void 0,function(){var y,_,S;return O(this,function(A){switch(A.label){case 0:if(t=g,o=m,n=m.loggerProvider,n.log("Installing @amplitude/plugin-page-view-tracking-browser"),i=!0,r){try{s=new Hs(r.sessionStorage)}catch{n?.debug("sessionStorage is not available in this environment.")}r.addEventListener("popstate",b),r.history.pushState=new Proxy(r.history.pushState,{apply:function(I,C,R){var P=oe(R,3),k=P[0],N=P[1],$=P[2];I.apply(C,[k,N,$]),i&&b()}})}return h()?(n.log("Tracking page view event"),y=void 0,s&&(y=Qt(),s.set(Ca,{pageViewId:y})),S=(_=t).track,[4,d(y)]):[3,2];case 1:S.apply(_,[A.sent()]),A.label=2;case 2:return[2]}})})},execute:function(m){return x(void 0,void 0,void 0,function(){var g,y,_;return O(this,function(S){switch(S.label){case 0:return a==="attribution"&&gI(m)?(n?.log("Enriching campaign event to page view event with campaign parameters"),g=void 0,s?[4,s.get(Ca)]:[3,2]):[3,4];case 1:y=S.sent(),g=y?.pageViewId,S.label=2;case 2:return[4,d(g)];case 3:_=S.sent(),m.event_type=_.event_type,m.event_properties=D(D({},m.event_properties),_.event_properties),S.label=4;case 4:return o&&m.event_type===l&&(o.pageCounter=o.pageCounter?o.pageCounter+1:1,m.event_properties=D(D({},m.event_properties),{"[Amplitude] Page Counter":o.pageCounter})),[2,m]}})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(m){return r&&(r.removeEventListener("popstate",b),i=!1),[2]})})}};return E},pI=function(){return x(void 0,void 0,void 0,function(){var e;return O(this,function(t){switch(t.label){case 0:return e=hI,[4,new og().parse()];case 1:return[2,e.apply(void 0,[t.sent()])]}})})},gI=function(e){if(e.event_type==="$identify"&&e.user_properties){var t=e.user_properties,r=t[hr.SET]||{},n=t[hr.UNSET]||{},i=Ee(Ee([],oe(Object.keys(r)),!1),oe(Object.keys(n)),!1);return Object.keys(Fs).every(function(o){return i.includes(o)})}return!1},mI=function(e,t,r){switch(e){case"pathOnly":{if(r=="")return!0;var n=new URL(t),i=new URL(r),o=n.origin+n.pathname,s=i.origin+i.pathname;return o!==s}default:return t!==r}},yI=function(){var e,t=[],r=function(l,f,d){l.addEventListener(f,d),t.push({element:l,type:f,handler:d})},n=function(){t.forEach(function(l){var f=l.element,d=l.type,h=l.handler;f?.removeEventListener(d,h)}),t=[]},i,o="@amplitude/plugin-form-interaction-tracking-browser",s="enrichment",a=function(l,f){return x(void 0,void 0,void 0,function(){var d,h;return O(this,function(v){return i=MA(l),d=function(){if(!f){l.loggerProvider.warn("Form interaction tracking requires a later version of @amplitude/analytics-browser. Form interaction events are not tracked.");return}if(!(typeof document>"u")){var p=new WeakSet,b=function(m){if(!p.has(m)){p.add(m);var g=!1;r(m,"change",function(){var y,_=sd(m);g||f.track(ed,(y={},y[Ta]=gn(m.id),y[Aa]=gn(m.name),y[Ia]=_,y)),g=!0}),r(m,"submit",function(y){var _,S,A=sd(m);if(g||f.track(ed,(_={},_[Ta]=gn(m.id),_[Aa]=gn(m.name),_[Ia]=A,_)),g=!0,i?.shouldTrackSubmit!==void 0)if(typeof i.shouldTrackSubmit=="function"&&typeof SubmitEvent<"u"&&y instanceof SubmitEvent)try{var I=i.shouldTrackSubmit(y);if(!I)return}catch{l.loggerProvider.warn("shouldTrackSubmit callback threw an error, proceeding with tracking.")}else l.loggerProvider.warn("shouldTrackSubmit is ignored because it is not a function or event is not a SubmitEvent.");f.track(WA,(S={},S[Ta]=gn(m.id),S[Aa]=gn(m.name),S[Ia]=A,S)),g=!1})}},E=Array.from(document.getElementsByTagName("form"));E.forEach(b),typeof MutationObserver<"u"&&(e=new MutationObserver(function(m){m.forEach(function(g){g.addedNodes.forEach(function(y){y.nodeName==="FORM"&&b(y),"querySelectorAll"in y&&typeof y.querySelectorAll=="function"&&Array.from(y.querySelectorAll("form")).map(b)})})}),e.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?d():(h=le(),h?h.addEventListener("load",d):l.loggerProvider.debug("Form interaction tracking is not installed because global is undefined.")),[2]})})},u=function(l){return x(void 0,void 0,void 0,function(){return O(this,function(f){return[2,l]})})},c=function(){return x(void 0,void 0,void 0,function(){return O(this,function(l){return e?.disconnect(),n(),[2]})})};return{name:o,type:s,setup:a,execute:u,teardown:c}},gn=function(e){if(typeof e=="string")return e},sd=function(e){var t=e.getAttribute("action");try{t=new URL(encodeURI(t??""),window.location.href).href}catch{}return t},bI=function(){var e,t=[],r=function(c,l,f){c.addEventListener(l,f),t.push({element:c,type:l,handler:f})},n=function(){t.forEach(function(c){var l=c.element,f=c.type,d=c.handler;l?.removeEventListener(f,d)}),t=[]},i="@amplitude/plugin-file-download-tracking-browser",o="enrichment",s=function(c,l){return x(void 0,void 0,void 0,function(){var f,d;return O(this,function(h){return f=function(){if(!l){c.loggerProvider.warn("File download tracking requires a later version of @amplitude/analytics-browser. File download events are not tracked.");return}if(!(typeof document>"u")){var v=function(E){var m;try{m=new URL(E.href,window.location.href)}catch{return}var g=p.exec(m.href),y=g?.[1];y&&r(E,"click",function(){var _;y&&l.track(GA,(_={},_[KA]=y,_[zA]=m.pathname,_[XA]=E.id,_[YA]=E.text,_[JA]=E.href,_))})},p=/\.(pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma)(\?.+)?$/,b=Array.from(document.getElementsByTagName("a"));b.forEach(v),typeof MutationObserver<"u"&&(e=new MutationObserver(function(E){E.forEach(function(m){m.addedNodes.forEach(function(g){g.nodeName==="A"&&v(g),"querySelectorAll"in g&&typeof g.querySelectorAll=="function"&&Array.from(g.querySelectorAll("a")).map(v)})})}),e.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?f():(d=le(),d?d.addEventListener("load",f):c.loggerProvider.debug("File download tracking is not installed because global is undefined.")),[2]})})},a=function(c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return[2,c]})})},u=function(){return x(void 0,void 0,void 0,function(){return O(this,function(c){return e?.disconnect(),n(),[2]})})};return{name:i,type:o,setup:s,execute:a,teardown:u}},ad=!1,EI=function(e){if(!(ad||e.defaultTracking!==void 0)){var t=`\`options.defaultTracking\` is set to undefined. This implicitly configures your Amplitude instance to track Page Views, Sessions, File Downloads, and Form Interactions. You can suppress this warning by explicitly setting a value to \`options.defaultTracking\`. The value must either be a boolean, to enable and disable all default events, or an object, for advanced configuration. For example: - -amplitude.init(, { - defaultTracking: true, -}); - -Visit https://www.docs.developers.amplitude.com/data/sdks/browser-2/#tracking-default-events for more details.`;e.loggerProvider.warn(t),ad=!0}},_I=function(){var e="@amplitude/plugin-network-checker-browser",t="before",r=le(),n=[],i=function(u,c){r?.addEventListener&&(r?.addEventListener(u,c),n.push({type:u,handler:c}))},o=function(){n.forEach(function(u){var c=u.type,l=u.handler;r?.removeEventListener(c,l)}),n=[]},s=function(u,c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return typeof navigator>"u"?(u.loggerProvider.debug("Network connectivity checker plugin is disabled because navigator is not available."),u.offline=!1,[2]):(u.offline=!navigator.onLine,i("online",function(){u.loggerProvider.debug("Network connectivity changed to online."),u.offline=!1,setTimeout(function(){c.flush()},u.flushIntervalMillis)}),i("offline",function(){u.loggerProvider.debug("Network connectivity changed to offline."),u.offline=!0}),[2])})})},a=function(){return x(void 0,void 0,void 0,function(){return O(this,function(u){return o(),[2]})})};return{name:e,type:t,setup:s,teardown:a}};function Ag(e){var t,r,n,i,o,s,a,u,c,l,f,d;if(!(typeof e!="object"||e===null)&&!Array.isArray(e)){var h=Object.keys(e);try{for(var v=ce(h),p=v.next();!p.done;p=v.next()){var b=p.value;try{var E=e[b];typeof E?.enabled=="boolean"&&(E.enabled?(delete E.enabled,Object.keys(E).length===0&&(e[b]=!0)):e[b]=!1),Ag(E)}catch{}}}catch($){t={error:$}}finally{try{p&&!p.done&&(r=v.return)&&r.call(v)}finally{if(t)throw t.error}}try{if(!((c=(u=(a=e.autocapture)===null||a===void 0?void 0:a.networkTracking)===null||u===void 0?void 0:u.captureRules)===null||c===void 0)&&c.length)try{for(var m=ce(e.autocapture.networkTracking.captureRules),g=m.next();!g.done;g=m.next()){var y=g.value;try{for(var _=(o=void 0,ce(["responseHeaders","requestHeaders"])),S=_.next();!S.done;S=_.next()){var A=S.value,I=(l=y[A])!==null&&l!==void 0?l:{},C=I.captureSafeHeaders,R=I.allowlist;if(!(!C&&!R)){if(R!==void 0&&!Array.isArray(R)){delete y[A];continue}y[A]=Ee(Ee([],oe(C?vs:[]),!1),oe(R??[]),!1)}}}catch($){o={error:$}}finally{try{S&&!S.done&&(s=_.return)&&s.call(_)}finally{if(o)throw o.error}}}}catch($){n={error:$}}finally{try{g&&!g.done&&(i=m.return)&&i.call(m)}finally{if(n)throw n.error}}}catch{}var P=(f=e.autocapture)===null||f===void 0?void 0:f.frustrationInteractions;P&&(P.rageClick&&(P.rageClicks=P.rageClick,delete P.rageClick),P.deadClick&&(P.deadClicks=P.deadClick,delete P.deadClick));try{var k=(d=e.autocapture)===null||d===void 0?void 0:d.elementInteractions;if(k&&typeof k=="object"&&(k.viewportContentUpdated===!0&&(k.viewportContentUpdated={}),k.viewportContentUpdated===!1&&(k.viewportContentUpdated={enabled:!1}),k.exposureDuration!==void 0)){var N=k.viewportContentUpdated;N===void 0?k.viewportContentUpdated={exposureDuration:k.exposureDuration}:typeof N=="object"&&N.exposureDuration===void 0&&N.enabled!==!1&&(N.exposureDuration=k.exposureDuration),delete k.exposureDuration}}catch{}}}function ud(e,t,r){var n,i,o=[];try{for(var s=ce(t??[]),a=s.next();!a.done;a=s.next()){var u=a.value;try{o.push(new RegExp(u))}catch(c){r.loggerProvider.warn("Invalid regex pattern: ".concat(u),c)}}}catch(c){n={error:c}}finally{try{a&&!a.done&&(i=s.return)&&i.call(s)}finally{if(n)throw n.error}}return e.concat(o)}function SI(e,t){var r,n,i,o,s,a,u;if(e){Ag(e);try{t.loggerProvider.debug("Update browser config with remote configuration:",JSON.stringify(e));var c=e;if(c&&"autocapture"in c){if(typeof c.autocapture=="boolean"&&(t.autocapture=c.autocapture),typeof c.autocapture=="object"&&c.autocapture!==null){var l=D({},c.autocapture);if(t.autocapture===void 0&&(t.autocapture=c.autocapture),typeof c.autocapture.elementInteractions=="object"&&c.autocapture.elementInteractions!==null&&(!((i=c.autocapture.elementInteractions.pageUrlAllowlistRegex)===null||i===void 0)&&i.length)){l.elementInteractions=D({},c.autocapture.elementInteractions);var f=l.elementInteractions,d=(o=f.pageUrlAllowlist)!==null&&o!==void 0?o:[],h=c.autocapture.elementInteractions.pageUrlAllowlistRegex;f.pageUrlAllowlist=ud(d,h,t),delete f.pageUrlAllowlistRegex}if(typeof c.autocapture.networkTracking=="object"&&c.autocapture.networkTracking!==null&&(!((s=c.autocapture.networkTracking.captureRules)===null||s===void 0)&&s.length)){l.networkTracking=D({},c.autocapture.networkTracking);var v=l.networkTracking,p=(a=v.captureRules)!==null&&a!==void 0?a:[];try{for(var b=ce(p),E=b.next();!E.done;E=b.next()){var m=E.value;m.urls=ud((u=m.urls)!==null&&u!==void 0?u:[],m.urlsRegex,t),delete m.urlsRegex}}catch(g){r={error:g}}finally{try{E&&!E.done&&(n=b.return)&&n.call(b)}finally{if(r)throw r.error}}}typeof t.autocapture=="boolean"&&(t.autocapture=D({attribution:t.autocapture,fileDownloads:t.autocapture,formInteractions:t.autocapture,pageViews:t.autocapture,sessions:t.autocapture,elementInteractions:t.autocapture,webVitals:t.autocapture,frustrationInteractions:t.autocapture},l)),typeof t.autocapture=="object"&&(t.autocapture=D(D({},t.autocapture),l))}t.defaultTracking=t.autocapture}"customEnrichment"in c&&c.customEnrichment!==null&&(t.customEnrichment=c.customEnrichment),t.loggerProvider.debug("Browser config after remote config update:",JSON.stringify(t))}catch(g){t.loggerProvider.error("Failed to apply remote configuration because of error: ",g)}}}var Ig="1.25.1",wI="@amplitude/plugin-autocapture-browser",TI="@amplitude/plugin-frustration-browser",Cg="[Amplitude] Element Clicked",AI="[Amplitude] Dead Click",II="[Amplitude] Rage Click",CI="[Amplitude] Error Click",kI="[Amplitude] Element Changed",RI="[Amplitude] Thrashed Cursor",PI="[Amplitude] Element ID",OI="[Amplitude] Element Class",ld="[Amplitude] Element Tag",cd="[Amplitude] Element Text",xI="[Amplitude] Element Hierarchy",NI="[Amplitude] Element Href",LI="[Amplitude] Element Position Left",MI="[Amplitude] Element Position Top",DI="[Amplitude] Element Aria Label",UI="[Amplitude] Element Attributes",FI="[Amplitude] Element Path",HI="[Amplitude] Element Parent Label",Iu="[Amplitude] Page URL",BI="[Amplitude] Page Title",kg="[Amplitude] Viewport Height",Rg="[Amplitude] Viewport Width",jI="[Amplitude] Max Page X",qI="[Amplitude] Max Page Y",Pg="[Amplitude] Page View ID",VI="https://cdn.amplitude.com/libs/visual-tagging-selector-1.0.0-alpha.js.gz",$I="amp-visual-tagging-selector-highlight",Og="data-amp-mask-attributes",WI=25,xg=128,GI="AMP_PAGE_VIEW",KI=18e3,zI=["input","select","textarea"],Ng=function(e,t){var r,n=(r=window?.getComputedStyle)===null||r===void 0?void 0:r.call(window,e);return n?.getPropertyValue("cursor")==="pointer"&&t==="click"},Lg=function(e){var t=e.pageUrlAllowlist,r=e.pageUrlExcludelist;return!(r&&r.length>0&&_u(window.location.href,r)||!_u(window.location.href,t))},Fn=function(e,t,r){return r===void 0&&(r=!1),function(n,i){var o,s,a=e.shouldTrackEventResolver,u=(s=(o=i?.tagName)===null||o===void 0?void 0:o.toLowerCase)===null||s===void 0?void 0:s.call(o);if(!u)return!1;if(a)return a(n,i);if(!Lg(e))return!1;var c=String(i?.getAttribute("type"))||"";if(typeof c=="string")switch(c.toLowerCase()){case"hidden":return!1;case"password":return!1}var l=Ng(i,n);if(r&&l)return!0;if(t){var f=t.some(function(d){var h;return!!(!((h=i?.matches)===null||h===void 0)&&h.call(i,d))});if(!f)return!1}switch(u){case"input":case"select":case"textarea":return n==="change"||n==="click";default:return l?!0:n==="click"}}},XI=function(e){var t,r,n,i=(r=(t=e?.tagName)===null||t===void 0?void 0:t.toLowerCase)===null||r===void 0?void 0:r.call(t),o=e instanceof HTMLElement?((n=e.getAttribute("contenteditable"))===null||n===void 0?void 0:n.toLowerCase())==="true":!1;return!zI.includes(i)&&!o},YI=function(e){return e?e.split(",").map(function(t){return t.trim()}).filter(function(t){return t.length>0&&t!=="id"&&t!=="class"}):[]},JI=function(e,t){return Object.entries(e).reduce(function(r,n){var i=oe(n,2),o=i[0],s=i[1];if(o.startsWith(t)){var a=o.replace(t,"");a&&(r[a]=s||"")}return r},{})},QI=function(e){return e==null||typeof e=="object"&&Object.keys(e).length===0||typeof e=="string"&&e.trim().length===0},fd=function(e){return Object.keys(e).reduce(function(t,r){var n=e[r];return QI(n)||(t[r]=n),t},{})},Mg=function(){var e;try{var t=le(),r=(e=t?.sessionStorage)===null||e===void 0?void 0:e.getItem(GI);if(!r)return;var n=JSON.parse(r);if(typeof n.pageViewId=="string")return n.pageViewId}catch{}},Nl=function(e,t){return e?t.some(function(r){var n;return(n=e?.matches)===null||n===void 0?void 0:n.call(e,r)})?e:Nl(e?.parentElement,t):null},no=function(e){return!(e.event.target===null||!e.closestTrackedAncestor)};function ZI(e){return e.type==="click"||e.type==="change"}var bs;(function(e){e[e.LEFT_OR_TOUCH_CONTACT=0]="LEFT_OR_TOUCH_CONTACT",e[e.MIDDLE=1]="MIDDLE",e[e.RIGHT=2]="RIGHT"})(bs||(bs={}));var dd="__AMPLITUDE_VISUAL_TAGGING__";function eC(e,t){var r=e;if(r[dd]!==!0){r[dd]=!0;var n=t.dataExtractor,i=t.isElementSelectable,o=t.cssSelectorAllowlist,s=t.actionClickAllowlist,a=null,u=function(l){e.notify({action:"element-selected",data:l})},c=function(l,f){l==="selector-mode-changed"?e.notify({action:"track-selector-mode-changed",data:f}):l==="selector-moved"&&e.notify({action:"track-selector-moved",data:f})};e.registerActionHandler("initialize-visual-tagging-selector",function(l){e.loadScriptOnce(VI).then(function(){var f;a=(f=window?.amplitudeVisualTaggingSelector)===null||f===void 0?void 0:f.call(window,{getEventTagProps:n.getEventTagProps,isElementSelectable:function(d){return i?i(l?.actionType||"click",d):!0},onTrack:c,onSelect:u,visualHighlightClass:$I,messenger:e,cssSelectorAllowlist:o,actionClickAllowlist:s,extractDataFromDataSource:n.extractDataFromDataSource,dataExtractor:n,diagnostics:{autocapture:{version:Ig}}}),e.notify({action:"selector-loaded"})}).catch(function(){var f;(f=e.logger)===null||f===void 0||f.warn("Failed to initialize visual tagging selector")})}),e.registerActionHandler("close-visual-tagging-selector",function(){var l;(l=a?.close)===null||l===void 0||l.call(a)})}}function tC(e){var t=e.amplitude,r=e.allObservables,n=e.shouldTrackEvent,i=e.evaluateTriggers,o=r.clickObservable,s=o.filter(no).filter(function(u){return n("click",u.closestTrackedAncestor)}).map(function(u){return i(u)}),a=s;return a.subscribe(function(u){t?.track(Cg,u.targetElementProperties)})}function rC(e){var t=e.amplitude,r=e.allObservables,n=e.getEventProperties,i=e.shouldTrackEvent,o=e.evaluateTriggers,s=r.changeObservable,a=s.filter(no).filter(function(u){return i("change",u.closestTrackedAncestor)}).map(function(u){return o(u)});return a.subscribe(function(u){t?.track(kI,n("change",u.closestTrackedAncestor))})}function nC(e){var t=e.amplitude,r=e.allObservables,n=e.options,i=e.getEventProperties,o=e.shouldTrackEvent,s=e.shouldTrackActionClick,a=r.clickObservable,u=r.mutationObservable,c=r.navigateObservable,l=a.filter(function(b){return!o("click",b.closestTrackedAncestor)}).map(function(b){var E=Nl(b.event.target,n.actionClickAllowlist);return b.closestTrackedAncestor=E,b.closestTrackedAncestor!==null&&(b.targetElementProperties=i(b.type,b.closestTrackedAncestor)),b}).filter(no).filter(function(b){return s("click",b.closestTrackedAncestor)}),f=c?un(u,c):u,d=un(l,f),h=null,v=null,p=Ol(d,function(b){if(h&&(clearTimeout(h),h=null),b.type==="click")return v=b,h=setTimeout(function(){h=null,v=null},500),Promise.resolve(null);if(v){var E=v;return v=null,Promise.resolve(E)}return Promise.resolve(null)});return p.subscribe(function(b){b&&t?.track(Cg,i("click",b.closestTrackedAncestor))})}function iC(e){var t=e.allObservables,r=t.scrollObservable,n={maxX:0,maxY:0},i=r.subscribe(function(){var o,s,a,u,c=le(),l=Math.floor((s=(o=c?.scrollX)!==null&&o!==void 0?o:c?.pageXOffset)!==null&&s!==void 0?s:0),f=Math.floor((u=(a=c?.scrollY)!==null&&a!==void 0?a:c?.pageYOffset)!==null&&u!==void 0?u:0);n.maxX=Math.max(n.maxX,l),n.maxY=Math.max(n.maxY,f)});return{unsubscribe:function(){i.unsubscribe()},getState:function(){return n},reset:function(){n.maxX=0,n.maxY=0}}}var Yn=le(),Dg=function(){return new et(function(e){var t=new MutationObserver(function(r){e.next(r)});return document.body&&t.observe(document.body,{childList:!0,attributes:!0,characterData:!0,subtree:!0}),function(){return t.disconnect()}})},Ug=function(e){return e===void 0&&(e="click"),new et(function(t){var r,n=function(i){t.next(i)};return(r=le())===null||r===void 0||r.document.addEventListener(e,n,{capture:!0}),function(){var i;(i=le())===null||i===void 0||i.document.removeEventListener(e,n,{capture:!0})}})},oC=function(){return new et(function(e){var t,r=function(n){e.next(n)};return(t=le())===null||t===void 0||t.addEventListener("scroll",r),function(){var n;(n=le())===null||n===void 0||n.removeEventListener("scroll",r)}})},sC=function(){return new et(function(e){var t=function(r){for(var n=[],i=1;i ")},IC=function(e,t,r){var n,i;if(e.nodeType!==Node.ELEMENT_NODE)return null;var o=e.getAttribute("id");if(t){if(o)return new fi(l(o),!0);var s=e.tagName.toLowerCase();if(s==="body"||s==="head"||s==="html")return new fi(s,!0)}var a=e.tagName.toLowerCase();if(o)return new fi(a+l(o),!0);var u=e.parentNode;if(!u||u.nodeType===Node.DOCUMENT_NODE)return new fi(a,!0);function c(P){var k=P.getAttribute("class");return k?k.split(/\s+/g).filter(Boolean).map(function(N){return"$"+N}):[]}function l(P){return"#"+CSS.escape(P)}for(var f=c(e),d=!1,h=!1,v=-1,p=-1,b=u.children,E=0;b&&(v===-1||!h)&&E=0;_--){var S=g[_];if(S){var A=YI(S.getAttribute(Og)),I=_===g.length-1?[]:(p=y.get(g[_+1]))!==null&&p!==void 0?p:new Set,C=new Set(Ee(Ee([],oe(I),!1),oe(A),!1));y.set(S,C)}}m=g.map(function(ee){var B;return wC(ee,(B=y.get(ee))!==null&&B!==void 0?B:new Set)});var R=function(ee){ee?.attrs&&Object.entries(ee.attrs).forEach(function(B){var z=oe(B,2),K=z[0],Q=z[1];ee.attrs&&(ee.attrs[K]=o.replaceSensitiveString(Q))})};try{for(var P=ce(m),k=P.next();!k.done;k=P.next()){var N=k.value;R(N)}}catch(ee){h={error:ee}}finally{try{k&&!k.done&&(v=P.return)&&v.call(P)}finally{if(h)throw h.error}}var $=performance.now();return(b=o.diagnosticsClient)===null||b===void 0||b.recordHistogram("autocapturePlugin.getHierarchy",$-E),m},this.getNearestLabel=function(d){var h=d.parentElement;if(!h)return"";var v;try{v=h.querySelector(":scope>span,h1,h2,h3,h4,h5,h6")}catch{v=null}return v?o.getText(v):o.getNearestLabel(h)},this.getElementPath=function(d){var h;if(!d)return"";var v=performance.now(),p=AC(d),b=performance.now();return(h=o.diagnosticsClient)===null||h===void 0||h.recordHistogram("autocapturePlugin.getElementPath",b-v),p},this.getEventProperties=function(d,h,v){var p,b,E,m,g=(E=(b=h?.tagName)===null||b===void 0?void 0:b.toLowerCase)===null||E===void 0?void 0:E.call(b),y=typeof h.getBoundingClientRect=="function"?h.getBoundingClientRect():{left:null,top:null},_=o.getHierarchy(h),S=(m=_[0])===null||m===void 0?void 0:m.attrs,A=o.getNearestLabel(h),I=JI(S??{},v),C=(p={},p[xI]=_,p[ld]=g,p[cd]=o.getText(h),p[LI]=y.left==null?null:Math.round(y.left),p[MI]=y.top==null?null:Math.round(y.top),p[UI]=I,p[FI]=o.getElementPath(h),p[HI]=A,p[Iu]=xr(window.location.href.split("?")[0]),p[BI]=Pl(o.replaceSensitiveString),p[kg]=window.innerHeight,p[Rg]=window.innerWidth,p),R=Mg();if(R&&(C[Pg]=R),C[PI]=h.getAttribute("id")||"",C[OI]=h.getAttribute("class"),C[DI]=S?.["aria-label"],g==="a"&&d==="click"&&h instanceof HTMLAnchorElement){var P=h.href.substring(0,xg);C[NI]=o.replaceSensitiveString(P)}return fd(C)},this.addTypeAndTimestamp=function(d,h){return{event:d,timestamp:Date.now(),type:h}},this.addAdditionalEventProperties=function(d,h,v,p,b){b===void 0&&(b=!1);var E=o.addTypeAndTimestamp(d,h);if(ZI(E)&&E.event.target!==null){if(b){var m=Ng(E.event.target,E.type);if(m)return E.closestTrackedAncestor=E.event.target,E.targetElementProperties=o.getEventProperties(E.type,E.closestTrackedAncestor,p),E}var g=Nl(E.event.target,v);return g&&(E.closestTrackedAncestor=g,E.targetElementProperties=o.getEventProperties(E.type,g,p)),E}return E},this.extractDataFromDataSource=function(d,h){if(d.sourceType==="DOM_ELEMENT"){var v=hC(d,h);return v?d.elementExtractType==="TEXT"?o.getText(v):d.elementExtractType==="ATTRIBUTE"&&d.attribute?v.getAttribute(d.attribute):void 0:void 0}},this.getTextWithMaskedDescendants=function(d){var h,v,p="[".concat(Tn,"], [contenteditable]");if(!d.querySelector(p))return d.innerText;var b="",E=Array.from(d.childNodes);try{for(var m=ce(E),g=m.next();!g.done;g=m.next()){var y=g.value;if(y.nodeType===Node.TEXT_NODE){b+=y.textContent||"";continue}if(y instanceof Element){if(y.hasAttribute(Tn)||y.hasAttribute("contenteditable")){b+=Nr;continue}b+=o.getTextWithMaskedDescendants(y)}}}catch(_){h={error:_}}finally{try{g&&!g.done&&(v=m.return)&&v.call(m)}finally{if(h)throw h.error}}return b},this.getText=function(d){var h=d.closest("[".concat(Tn,"]"))!==null;if(h)return Nr;var v="";return d.querySelector("[".concat(Tn,"], [contenteditable]"))?v=o.getTextWithMaskedDescendants(d):v=d.innerText||"",o.replaceSensitiveString(v.substring(0,255)).replace(/\s+/g," ").trim()},this.getEventTagProps=function(d){var h,v,p;if(!d)return{};var b=(p=(v=d?.tagName)===null||v===void 0?void 0:v.toLowerCase)===null||p===void 0?void 0:p.call(v),E=(h={},h[ld]=b,h[cd]=o.getText(d),h[Iu]=window.location.href.split("?")[0],h);return fd(E)},this.diagnosticsClient=r?.diagnosticsClient;var a=(s=t.maskTextRegex)!==null&&s!==void 0?s:[],u=[];try{for(var c=ce(a),l=c.next();!l.done;l=c.next()){var f=l.value;if(u.length>=WI)break;if(f instanceof RegExp)u.push(f);else if("pattern"in f&&typeof f.pattern=="string")try{u.push(new RegExp(f.pattern,"i"))}catch{}}}catch(d){n={error:d}}finally{try{l&&!l.done&&(i=c.return)&&i.call(c)}finally{if(n)throw n.error}}this.additionalMaskTextPatterns=u}return e})();function CC(e){var t=e.allObservables,r=e.onExposure,n=e.dataExtractor,i=e.exposureDuration,o=i===void 0?Zp:i,s=new Map,a=new Map,u=t.exposureObservable,c=u.subscribe(function(l){var f=l,d=f.target;if(f.isIntersecting){if(!s.get(d)){var h=setTimeout(function(){s.set(d,!0);var v=n.getElementPath(d);r(v),a.set(d,null)},o);a.set(d,h)}}else if(!f.isIntersecting&&f.intersectionRatio<1){var h=a.get(d);h&&(clearTimeout(h),a.set(d,null))}});return{unsubscribe:function(){c.unsubscribe()},reset:function(){a.forEach(function(l){l&&clearTimeout(l)}),a.clear(),s.clear()}}}function kC(e){var t,r,n,i,o=e.amplitude,s=e.scrollTracker,a=e.currentElementExposed,u=e.elementExposedForPage,c=e.exposureTracker,l=e.isPageEnd,f=e.lastScroll,d=s.getState(),h=le(),v=(r=h?.innerWidth)!==null&&r!==void 0?r:0,p=(n=h?.innerHeight)!==null&&n!==void 0?n:0,b=(t={},t[Iu]=(i=h?.location)===null||i===void 0?void 0:i.href,t[jI]=d.maxX+v,t[qI]=d.maxY+p,t[kg]=p,t[Rg]=v,t["[Amplitude] Element Exposed"]=Array.from(a),t),E=Mg();if(E&&(b[Pg]=E),a.size===0&&d.maxX===f.maxX&&d.maxY===f.maxY){l&&(s.reset(),u.clear(),c?.reset());return}o?.track("[Amplitude] Viewport Content Updated",b),f.maxX=d.maxX,f.maxY=d.maxY,a.clear(),l&&(s.reset(),u.clear(),c?.reset())}function RC(e,t,r,n){if(!t.has(e)){t.add(e),r.add(e);var i=Array.from(r),o=JSON.stringify(i);o.length>=KI&&n(!1)}}var ft;(function(e){e.ClickObservable="clickObservable",e.ChangeObservable="changeObservable",e.NavigateObservable="navigateObservable",e.MutationObservable="mutationObservable",e.ScrollObservable="scrollObservable",e.ExposureObservable="exposureObservable",e.BrowserErrorObservable="browserErrorObservable",e.SelectionObservable="selectionObservable",e.MouseMoveObservable="mouseMoveObservable"})(ft||(ft={}));var PC=function(e,t){var r,n,i,o,s,a,u,c,l,f,d,h;e===void 0&&(e={}),t?.diagnosticsClient.setTag("plugin.autocapture.version",Ig);var v=e.dataAttributePrefix,p=v===void 0?Cl:v,b=e.visualTaggingOptions,E=b===void 0?{enabled:!0}:b;e.cssSelectorAllowlist=(r=e.cssSelectorAllowlist)!==null&&r!==void 0?r:Jp,e.actionClickAllowlist=(n=e.actionClickAllowlist)!==null&&n!==void 0?n:Qp,e.debounceTime=(i=e.debounceTime)!==null&&i!==void 0?i:0;var m=((o=e.viewportContentUpdated)===null||o===void 0?void 0:o.enabled)!==!1,g=(u=(a=(s=e.viewportContentUpdated)===null||s===void 0?void 0:s.exposureDuration)!==null&&a!==void 0?a:e.exposureDuration)!==null&&u!==void 0?u:Zp;e.viewportContentUpdated=D(D({},e.viewportContentUpdated),{exposureDuration:g}),e.pageUrlExcludelist=(c=e.pageUrlExcludelist)===null||c===void 0?void 0:c.reduce(function(Q,ae){if(typeof ae=="string"&&Q.push(ae),ae instanceof RegExp&&Q.push(ae),typeof ae=="object"&&ae!==null&&"pattern"in ae)try{Q.push(new RegExp(ae.pattern))}catch(fe){return console.warn("Invalid regex pattern: ".concat(ae.pattern),fe),Q}return Q},[]);var y=wI,_="enrichment",S=[],A=new Fg(e,t),I=new Set,C=new Set,R,P=function(){var Q,ae=Xt(Ug().map(function(me){return A.addAdditionalEventProperties(me,"click",e.cssSelectorAllowlist,p)})),fe=Xt(new et(function(me){var M,Z=function(X){var re=A.addAdditionalEventProperties(X,"change",e.cssSelectorAllowlist,p);me.next(re)};return(M=le())===null||M===void 0||M.document.addEventListener("change",Z,{capture:!0}),function(){var X;return(X=le())===null||X===void 0?void 0:X.document.removeEventListener("change",Z)}})),pe;window.navigation&&(pe=Xt(new et(function(me){var M=function(Z){var X=A.addAdditionalEventProperties(Z,"navigate",e.cssSelectorAllowlist,p);me.next(X)};return window.navigation.addEventListener("navigate",M),function(){window.navigation.removeEventListener("navigate",M)}})));var ye=Xt(Dg().map(function(me){return A.addAdditionalEventProperties(me,"mutation",e.cssSelectorAllowlist,p)})),be=oC(),de=aC(ye,e.cssSelectorAllowlist);return Q={},Q[ft.ChangeObservable]=fe,Q[ft.ClickObservable]=ae,Q[ft.MutationObservable]=ye,Q[ft.NavigateObservable]=pe,Q[ft.ScrollObservable]=be,Q[ft.ExposureObservable]=de,Q},k=hd(Object.values((f=(l=e.pageActions)===null||l===void 0?void 0:l.labeledEvents)!==null&&f!==void 0?f:{})),N=vd((h=(d=e.pageActions)===null||d===void 0?void 0:d.triggers)!==null&&h!==void 0?h:[]),$=yC(k,N,A,e),ee=function(Q){var ae,fe;Q&&(e.pageActions=D(D({},e.pageActions),Q),k=hd(Object.values((ae=e.pageActions.labeledEvents)!==null&&ae!==void 0?ae:{})),N=vd((fe=e.pageActions.triggers)!==null&&fe!==void 0?fe:[]),$.update(k,N,e))},B=function(Q,ae){return x(void 0,void 0,void 0,function(){var fe,pe,ye,be,de,me,M,Z,X,re,F,ge,w,T,L,H,U,q,te,G,Y;return O(this,function(V){return typeof document>"u"?[2]:(fe=!1,pe={maxX:void 0,maxY:void 0},Q.fetchRemoteConfig&&(Q.remoteConfigClient?Q.remoteConfigClient.subscribe("configs.analyticsSDK.pageActions","all",function(W){ee(W)}):Q.loggerProvider.debug("Remote config client is not provided, skipping remote config fetch")),ye=Fn(e,e.cssSelectorAllowlist),be=Fn(e,e.actionClickAllowlist),de=P(),me=tC({allObservables:de,amplitude:ae,shouldTrackEvent:ye,evaluateTriggers:$.evaluate.bind($)}),S.push(me),M=rC({allObservables:de,getEventProperties:function(){for(var W=[],j=0;jpd||e.xMax-e.xMin>pd}function md(e){if(e.length===0)return null;var t=e[0],r=e[e.length-1],n=D({"[Amplitude] Begin Time":new Date(t.timestamp).toISOString(),"[Amplitude] End Time":new Date(r.timestamp).toISOString(),"[Amplitude] Duration":r.timestamp-t.timestamp,"[Amplitude] Clicks":e.map(function(i){return{X:i.event.pageX,Y:i.event.pageY,Time:i.timestamp}}),"[Amplitude] Click Count":e.length},t.targetElementProperties);return{rageClickEvent:n,time:t.timestamp}}function LC(e,t){var r=Math.max(0,e.length-Hg+1),n=e[r];return t.timestamp-n.timestamp>=Bg}function MC(e,t){return e.length>0&&e[e.length-1].closestTrackedAncestor!==t.closestTrackedAncestor}function DC(e){var t=this,r=e.amplitude,n=e.allObservables,i=e.shouldTrackRageClick,o=n.clickObservable,s=n.selectionObservable,a=[],u={},c=null;function l(v){a=[],u={},v&&(gd(u,v),a.push(v))}var f=Ol(o.filter(function(v){return i("click",v.closestTrackedAncestor)}),function(v){return x(t,void 0,void 0,function(){var p;return O(this,function(b){return gd(u,v),p=null,a.length===0||MC(a,v)||LC(a,v)||u.isOutOfBounds?(c&&(p=md(a)),l(v)):a.push(v),c&&(clearTimeout(c.timerId),c.resolve(p),c=null),a.length>=Hg?[2,new Promise(function(E){c={resolve:E,timerId:setTimeout(function(){E(md(a))},Bg)}})]:[2,null]})})}),d=s?.subscribe(function(){l()}),h=f.subscribe(function(v){v!==null&&r.track(II,v.rageClickEvent,{time:v.time})});return{unsubscribe:function(){h.unsubscribe(),d?.unsubscribe()}}}var UC=2e3;function FC(e){var t=e.amplitude,r=e.allObservables,n=e.shouldTrackErrorClick,i=r.clickObservable,o=r.browserErrorObservable,s=i.filter(function(l){return no(l)&&n("click",l.closestTrackedAncestor)&&l.event.target instanceof Element&&l.event.target.closest('a[target="_blank"]')===null&&l.event.button===bs.LEFT_OR_TOUCH_CONTACT}),a=null,u=null,c=function(){a!==null&&(clearTimeout(a),a=null),u=null};return un(s,o).subscribe(function(l){var f;if(l.type==="click"){c(),u=l,a=setTimeout(c,UC);return}l.type==="error"&&u&&(t.track(CI,D((f={},f["[Amplitude] Kind"]=l.event.kind,f["[Amplitude] Message"]=l.event.message,f["[Amplitude] Stack"]=l.event.stack,f["[Amplitude] Filename"]=l.event.filename,f["[Amplitude] Line Number"]=l.event.lineNumber,f["[Amplitude] Column Number"]=l.event.columnNumber,f),u.targetElementProperties)),c())})}var Kt;(function(e){e.INCREASING="increasing",e.DECREASING="decreasing"})(Kt||(Kt={}));var Qr;(function(e){e.X="x",e.Y="y"})(Qr||(Qr={}));var HC=function(e){var t=e.allWindowObservables,r=t.mouseMoveObservable;return new et(function(n){var i=null,o=null,s=null;return r.subscribe(function(a){var u={x:a.clientX,y:a.clientY};if(i===null){i=u;return}u.x>i.x?(o===Kt.DECREASING&&n.next(Qr.X),o=Kt.INCREASING):u.xi.y?(s===Kt.DECREASING&&n.next(Qr.Y),s=Kt.INCREASING):u.yn&&r.shift()}function yd(e){var t=e.changes,r=e.changesThreshold,n=e.thresholdMs;if(t.length"u"?[2]:(_=p(),a&&(S=Fn(e,c),A=DC({allObservables:_,amplitude:y,shouldTrackRageClick:S}),i.push(A)),s&&(I=Fn(e,l),C=NC({amplitude:y,allObservables:_,getEventProperties:function(z,K){return h.getEventProperties(z,K,d)},shouldTrackDeadClick:I}),i.push(C)),o&&(R=Fn(e,f),P=FC({amplitude:y,allObservables:_,shouldTrackErrorClick:R}),i.push(P)),u&&(k=void 0,N=void 0,typeof e.thrashedCursor=="object"&&(k=e.thrashedCursor.directionChanges,N=e.thrashedCursor.threshold,k&&kwo&&(g.loggerProvider.warn("'thrashedCursor.threshold' of ".concat(N," is above the maximum of ").concat(wo,", setting to ").concat(wo)),N=wo)),$=VC({amplitude:y,options:e,allObservables:_,directionChanges:k,thresholdMs:N}),i.push($)),(ee=g?.loggerProvider)===null||ee===void 0||ee.log("".concat(r," has been successfully added.")),[2])})})},E=function(g){return x(void 0,void 0,void 0,function(){return O(this,function(y){return[2,g]})})},m=function(){return x(void 0,void 0,void 0,function(){var g,y,_,S,A;return O(this,function(I){try{for(g=ce(i),y=g.next();!y.done;y=g.next())_=y.value,_.unsubscribe()}catch(C){S={error:C}}finally{try{y&&!y.done&&(A=g.return)&&A.call(g)}finally{if(S)throw S.error}}return[2]})})};return{name:r,type:n,setup:b,execute:E,teardown:m}},WC="@amplitude/plugin-network-capture-browser",Vg="[Amplitude] Network Request",$g="500-599";function qo(e,t){var r=t.replace(/[-[\]{}()+?.,\\^$|#\s]/g,"\\$&"),n="^"+r.replace(/\*/g,".*")+"$",i=new RegExp(n);return i.test(e)}function Wg(e,t){var r,n,i=t.split(",");try{for(var o=ce(i),s=o.next();!s.done;s=o.next()){var a=s.value,u=oe(a.split("-").map(Number),2),c=u[0],l=u[1];if(e===c&&l===void 0||e>=c&&e<=l)return!0}}catch(f){r={error:f}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return!1}function GC(e,t,r,n,i){if(!(e.hosts&&!e.hosts.find(function(s){return qo(t,s)}))&&!(n&&e.urls&&!_u(n,e.urls))&&!(i&&e.methods&&!e.methods.find(function(s){return i.toLowerCase()===s.toLowerCase()||s==="*"}))){if(r||r===0){var o=e.statusCodeRange||$g;if(!Wg(r,o))return!1}return!0}}function Gg(e){var t;if(e)try{var r=(t=le())===null||t===void 0?void 0:t.location.href,n=new URL(e,r),i=n.searchParams.toString(),o=n.hash.replace("#",""),s=n.href,a=n.host;n.hash="",n.search="";var u=n.href;return{query:i,fragment:o,href:s,hrefWithoutQueryOrHash:u,host:a}}catch{return}}function KC(e,t){if(e.includes("amplitude.com"))try{var r=t.body;if(typeof r!="string")return!1;var n=JSON.parse(r),i=n.events;if(i.find(function(o){return o.event_type===Vg}))return!0}catch{}return!1}function _d(e){if(typeof e!="object"||e===null){if(e)return Ee([],oe(vs),!1);if(e===void 0){var t=void 0;return t}return}if(e.length!==0)return e}function Sd(e){var t,r;return!(!((t=e?.allowlist)===null||t===void 0)&&t.length)&&!(!((r=e?.blocklist)===null||r===void 0)&&r.length)}function zC(e,t){var r;t===void 0&&(t={});var n=Gg(e.url);if(!n)return!1;var i=n.host;if(t.ignoreAmplitudeRequests!==!1&&(qo(i,"*.amplitude.com")||qo(i,"amplitude.com"))||!((r=t.ignoreHosts)===null||r===void 0)&&r.find(function(s){return qo(i,s)})||!t.captureRules&&e.status!==void 0&&!Wg(e.status,$g))return!1;if(t.captureRules){var o;if(Ee([],oe(t.captureRules),!1).reverse().find(function(s){if(o=GC(s,i,e.status,e.url,e.method),o){var a=_d(s.responseHeaders);if(e.responseWrapper&&a){var u=e.responseWrapper.headers(a);u&&(e.responseHeaders=u)}var c=_d(s.requestHeaders);if(e.requestWrapper&&c){var l=e.requestWrapper.headers(c);l&&(e.requestHeaders=l)}e.responseWrapper&&s.responseBody&&!Sd(s.responseBody)&&(e.responseBodyJson=e.responseWrapper.json(s.responseBody.allowlist,s.responseBody.blocklist)),e.requestWrapper&&s.requestBody&&!Sd(s.requestBody)&&(e.requestBodyJson=e.requestWrapper.json(s.requestBody.allowlist,s.requestBody.blocklist))}return o!==void 0}),!o)return!1}return!(e.requestWrapper&&KC(i,e.requestWrapper))}function XC(e,t,r,n){return x(this,void 0,void 0,function(){var i,o,s;return O(this,function(a){switch(a.label){case 0:return t.requestBodyJson||t.responseBodyJson?[4,Promise.all([t.requestBodyJson,t.responseBodyJson])]:[3,2];case 1:if(i=oe.apply(void 0,[a.sent(),2]),o=i[0],s=i[1],o)try{e["[Amplitude] Request Body"]=JSON.stringify(o)}catch(u){n?.debug("Failed to stringify request body",u)}if(s)try{e["[Amplitude] Response Body"]=JSON.stringify(s)}catch{n?.debug("Failed to stringify response body")}a.label=2;case 2:return r?.track(Vg,e),[2]}})})}function YC(e){var t=e.allObservables,r=e.networkTrackingOptions,n=e.amplitude,i=e.loggerProvider,o=t.networkObservable,s=o.filter(function(a){return zC(a.event,r)});return s.subscribe(function(a){var u,c,l,f=a.event,d=Gg(f.url);if(d){var h=(c=f.responseWrapper)===null||c===void 0?void 0:c.bodySize,v=(l=f.requestWrapper)===null||l===void 0?void 0:l.bodySize,p=(u={},u["[Amplitude] URL"]=d.hrefWithoutQueryOrHash,u["[Amplitude] URL Query"]=d.query,u["[Amplitude] URL Fragment"]=d.fragment,u["[Amplitude] Request Method"]=f.method,u["[Amplitude] Status Code"]=f.status,u["[Amplitude] Start Time"]=f.startTime,u["[Amplitude] Completion Time"]=f.endTime,u["[Amplitude] Duration"]=f.duration,u["[Amplitude] Request Body Size"]=v,u["[Amplitude] Response Body Size"]=h,u["[Amplitude] Request Type"]=f.type,u["[Amplitude] Request Headers"]=f.requestHeaders,u["[Amplitude] Response Headers"]=f.responseHeaders,u);XC(p,f,n,i)}})}var ku;(function(e){e.NetworkObservable="networkObservable"})(ku||(ku={}));var wd,JC=function(e){e===void 0&&(e={});var t=WC,r="enrichment",n,i=function(c,l){var f={event:c,timestamp:Date.now(),type:l};return f},o=function(){var c,l=new et(function(f){var d=new cA(function(h){var v=i(h,"network");f.next(v)});return Gf.subscribe(d,n),function(){Gf.unsubscribe(d)}});return c={},c[ku.NetworkObservable]=l,c},s=function(c,l){return x(void 0,void 0,void 0,function(){var f;return O(this,function(d){return typeof document>"u"?[2]:(f=o(),n=c?.loggerProvider,wd=YC({allObservables:f,networkTrackingOptions:e,amplitude:l,loggerProvider:n}),n?.log("".concat(t," has been successfully added.")),[2])})})},a=function(c){return x(void 0,void 0,void 0,function(){return O(this,function(l){return[2,c]})})},u=function(){return x(void 0,void 0,void 0,function(){return O(this,function(c){return wd.unsubscribe(),[2]})})};return{name:t,type:r,setup:s,execute:a,teardown:u}},QC="web-vitals-browser",ZC="[Amplitude] Web Vitals";let Kg=-1;const ti=e=>{addEventListener("pageshow",(t=>{t.persisted&&(Kg=t.timeStamp,e(t))}),!0)},Zt=(e,t,r,n)=>{let i,o;return s=>{t.value>=0&&(s||n)&&(o=t.value-(i??0),(o||i===void 0)&&(i=t.value,t.delta=o,t.rating=((a,u)=>a>u[1]?"poor":a>u[0]?"needs-improvement":"good")(t.value,r),e(t)))}},Ll=e=>{requestAnimationFrame((()=>requestAnimationFrame((()=>e()))))},Ml=()=>{const e=performance.getEntriesByType("navigation")[0];if(e&&e.responseStart>0&&e.responseStartMl()?.activationStart??0,er=(e,t=-1)=>{const r=Ml();let n="navigate";return Kg>=0?n="back-forward-cache":r&&(document.prerendering||io()>0?n="prerender":document.wasDiscarded?n="restore":r.type&&(n=r.type.replace(/_/g,"-"))),{name:e,value:t,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:n}},Ra=new WeakMap;function Dl(e,t){return Ra.get(e)||Ra.set(e,new t),Ra.get(e)}class ek{t;i=0;o=[];h(t){if(t.hadRecentInput)return;const r=this.o[0],n=this.o.at(-1);this.i&&r&&n&&t.startTime-n.startTime<1e3&&t.startTime-r.startTime<5e3?(this.i+=t.value,this.o.push(t)):(this.i=t.value,this.o=[t]),this.t?.(t)}}const oo=(e,t,r={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(e)){const n=new PerformanceObserver((i=>{Promise.resolve().then((()=>{t(i.getEntries())}))}));return n.observe({type:e,buffered:!0,...r}),n}}catch{}},Ul=e=>{let t=!1;return()=>{t||(e(),t=!0)}};let An=-1;const zg=new Set,Td=()=>document.visibilityState!=="hidden"||document.prerendering?1/0:0,Ru=e=>{if(document.visibilityState==="hidden"){if(e.type==="visibilitychange")for(const t of zg)t();isFinite(An)||(An=e.type==="visibilitychange"?e.timeStamp:0,removeEventListener("prerenderingchange",Ru,!0))}},js=()=>{if(An<0){const e=io();An=(document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((r=>r.name==="hidden"&&r.startTime>e))[0]?.startTime)??Td(),addEventListener("visibilitychange",Ru,!0),addEventListener("prerenderingchange",Ru,!0),ti((()=>{setTimeout((()=>{An=Td()}))}))}return{get firstHiddenTime(){return An},onHidden(e){zg.add(e)}}},qs=e=>{document.prerendering?addEventListener("prerenderingchange",(()=>e()),!0):e()},Ad=[1800,3e3],Xg=(e,t={})=>{qs((()=>{const r=js();let n,i=er("FCP");const o=oo("paint",(s=>{for(const a of s)a.name==="first-contentful-paint"&&(o.disconnect(),a.startTime{i=er("FCP"),n=Zt(e,i,Ad,t.reportAllChanges),Ll((()=>{i.value=performance.now()-s.timeStamp,n(!0)}))})))}))},Id=[.1,.25],tk=(e,t={})=>{const r=js();Xg(Ul((()=>{let n,i=er("CLS",0);const o=Dl(t,ek),s=u=>{for(const c of u)o.h(c);o.i>i.value&&(i.value=o.i,i.entries=o.o,n())},a=oo("layout-shift",s);a&&(n=Zt(e,i,Id,t.reportAllChanges),r.onHidden((()=>{s(a.takeRecords()),n(!0)})),ti((()=>{o.i=0,i=er("CLS",0),n=Zt(e,i,Id,t.reportAllChanges),Ll((()=>n()))})),setTimeout(n))})))};let Yg=0,Pa=1/0,To=0;const rk=e=>{for(const t of e)t.interactionId&&(Pa=Math.min(Pa,t.interactionId),To=Math.max(To,t.interactionId),Yg=To?(To-Pa)/7+1:0)};let Pu;const Cd=()=>Pu?Yg:performance.interactionCount??0,nk=()=>{"interactionCount"in performance||Pu||(Pu=oo("event",rk,{type:"event",buffered:!0,durationThreshold:0}))};let kd=0,ik=class{u=[];l=new Map;m;p;v(){kd=Cd(),this.u.length=0,this.l.clear()}L(){const t=Math.min(this.u.length-1,Math.floor((Cd()-kd)/50));return this.u[t]}h(t){if(this.m?.(t),!t.interactionId&&t.entryType!=="first-input")return;const r=this.u.at(-1);let n=this.l.get(t.interactionId);if(n||this.u.length<10||t.duration>r.P){if(n?t.duration>n.P?(n.entries=[t],n.P=t.duration):t.duration===n.P&&t.startTime===n.entries[0].startTime&&n.entries.push(t):(n={id:t.interactionId,entries:[t],P:t.duration},this.l.set(n.id,n),this.u.push(n)),this.u.sort(((i,o)=>o.P-i.P)),this.u.length>10){const i=this.u.splice(10);for(const o of i)this.l.delete(o.id)}this.p?.(n)}}};const Jg=e=>{const t=globalThis.requestIdleCallback||setTimeout;document.visibilityState==="hidden"?e():(e=Ul(e),addEventListener("visibilitychange",e,{once:!0,capture:!0}),t((()=>{e(),removeEventListener("visibilitychange",e,{capture:!0})})))},Rd=[200,500],ok=(e,t={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const r=js();qs((()=>{nk();let n,i=er("INP");const o=Dl(t,ik),s=u=>{Jg((()=>{for(const l of u)o.h(l);const c=o.L();c&&c.P!==i.value&&(i.value=c.P,i.entries=c.entries,n())}))},a=oo("event",s,{durationThreshold:t.durationThreshold??40});n=Zt(e,i,Rd,t.reportAllChanges),a&&(a.observe({type:"first-input",buffered:!0}),r.onHidden((()=>{s(a.takeRecords()),n(!0)})),ti((()=>{o.v(),i=er("INP"),n=Zt(e,i,Rd,t.reportAllChanges)})))}))};let sk=class{m;h(t){this.m?.(t)}};const Pd=[2500,4e3],ak=(e,t={})=>{qs((()=>{const r=js();let n,i=er("LCP");const o=Dl(t,sk),s=u=>{t.reportAllChanges||(u=u.slice(-1));for(const c of u)o.h(c),c.startTime{s(a.takeRecords()),a.disconnect(),n(!0)})),c=l=>{l.isTrusted&&(Jg(u),removeEventListener(l.type,c,{capture:!0}))};for(const l of["keydown","click","visibilitychange"])addEventListener(l,c,{capture:!0});ti((l=>{i=er("LCP"),n=Zt(e,i,Pd,t.reportAllChanges),Ll((()=>{i.value=performance.now()-l.timeStamp,n(!0)}))}))}}))},Od=[800,1800],Ou=e=>{document.prerendering?qs((()=>Ou(e))):document.readyState!=="complete"?addEventListener("load",(()=>Ou(e)),!0):setTimeout(e)},uk=(e,t={})=>{let r=er("TTFB"),n=Zt(e,r,Od,t.reportAllChanges);Ou((()=>{const i=Ml();i&&(r.value=Math.max(i.responseStart-io(),0),r.entries=[i],n(!0),ti((()=>{r=er("TTFB",0),n=Zt(e,r,Od,t.reportAllChanges),n(!0)})))}))};function lk(e){var t,r=((t=e.entries[0])===null||t===void 0?void 0:t.startTime)||0;return performance.timeOrigin+r}function di(e){return{value:e.value,rating:e.rating,delta:e.delta,navigationType:e.navigationType,id:e.id,timestamp:Math.floor(lk(e)),navigationStart:Math.floor(performance.timeOrigin)}}var ck=function(){var e=null,t=le(),r=t?.document,n=t?.location,i=function(a,u){return x(void 0,void 0,void 0,function(){var c,l;return O(this,function(f){return r===void 0?[2]:(c=xr(n?.href||"",a.loggerProvider),l={"[Amplitude] Page Domain":n?.hostname||"","[Amplitude] Page Location":c,"[Amplitude] Page Path":xr(n?.pathname||"",a.loggerProvider),"[Amplitude] Page Title":typeof document<"u"&&document.title||"","[Amplitude] Page URL":xr(c.split("?")[0],a.loggerProvider)},ak(function(d){l["[Amplitude] LCP"]=di(d)}),Xg(function(d){l["[Amplitude] FCP"]=di(d)}),ok(function(d){l["[Amplitude] INP"]=di(d)}),tk(function(d){l["[Amplitude] CLS"]=di(d)}),uk(function(d){l["[Amplitude] TTFB"]=di(d)}),e=function(){r.visibilityState==="hidden"&&e&&(u.track(ZC,l),r.removeEventListener("visibilitychange",e),e=null)},r.addEventListener("visibilitychange",e),[2])})})},o=function(a){return x(void 0,void 0,void 0,function(){return O(this,function(u){return[2,a]})})},s=function(){return x(void 0,void 0,void 0,function(){return O(this,function(a){return e&&r?.removeEventListener("visibilitychange",e),[2]})})};return{name:QC,type:"enrichment",setup:i,execute:o,teardown:s}},xd=(function(){function e(t,r){var n;this.shouldTrackNewCampaign=!1,this.options=D({initialEmptyValue:"EMPTY",resetSessionOnNewCampaign:!1,excludeReferrers:oI(((n=r.cookieOptions)===null||n===void 0?void 0:n.domain)||r.topLevelDomain),optOut:r.optOut},t),this.storage=r.cookieStorage,this.storageKey=Ff(r.apiKey,"MKTG"),this.webExpStorageKey=Ff(r.apiKey,"MKTG_ORIGINAL"),this.currentCampaign=Fs,this.sessionTimeout=r.sessionTimeout,this.lastEventTime=r.lastEventTime,this.logger=r.loggerProvider,this.topLevelDomain=r.topLevelDomain,r.loggerProvider.log("Installing web attribution tracking.")}return e.prototype.init=function(){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:return this.options.optOut?[2]:[4,this.fetchCampaign()];case 1:return r=oe.apply(void 0,[n.sent(),2]),this.currentCampaign=r[0],this.previousCampaign=r[1],t=this.lastEventTime?qp(this.sessionTimeout,this.lastEventTime):!0,tI(this.currentCampaign,this.previousCampaign,this.options,this.logger,t,this.topLevelDomain)?(this.shouldTrackNewCampaign=!0,[4,this.storage.set(this.storageKey,this.currentCampaign)]):[3,3];case 2:n.sent(),n.label=3;case 3:return[2]}})})},e.prototype.fetchCampaign=function(){return x(this,void 0,void 0,function(){var t;return O(this,function(r){switch(r.label){case 0:return[4,this.storage.get(this.webExpStorageKey)];case 1:return t=r.sent(),t?[4,this.storage.remove(this.webExpStorageKey)]:[3,3];case 2:r.sent(),r.label=3;case 3:return[4,Promise.all([t||new og().parse(),this.storage.get(this.storageKey)])];case 4:return[2,r.sent()]}})})},e.prototype.generateCampaignEvent=function(t){this.shouldTrackNewCampaign=!1;var r=iI(this.currentCampaign,this.options);return t&&(r.event_id=t),r},e.prototype.shouldSetSessionIdOnNewCampaign=function(){return this.shouldTrackNewCampaign&&!!this.options.resetSessionOnNewCampaign},e})(),Ao="AMP_CURRENT_PAGE",Io="AMP_PREVIOUS_PAGE",mn="AMP_URL_INFO",Li;(function(e){e.Direct="direct",e.Internal="internal",e.External="external"})(Li||(Li={}));var fk=new Set([_t.IDENTIFY,_t.GROUP_IDENTIFY,_t.REVENUE]),dk=function(e){var t={},r=t.internalDomains,n=r===void 0?[]:r,i=le(),o=void 0,s=!1,a=void 0,u=!1,c=!1,l=function(p){var b;try{var E=xr(p,a);b=new URL(E).hostname}catch(m){a?.error("Could not parse URL: ",m)}return b},f=function(p){var b=typeof location<"u"&&location.hostname||"",E=p?l(p):void 0;if(!E)return Li.Direct;var m=n.some(function(y){return b.indexOf(y)!==-1}),g=n.some(function(y){return E.indexOf(y)!==-1});return b===E||g&&m?Li.Internal:Li.External},d=function(){return x(void 0,void 0,void 0,function(){var p,b,E,m,g;return O(this,function(y){switch(y.label){case 0:return o&&s?[4,o.get(mn)]:[3,3];case 1:return p=y.sent(),b=xr(typeof location<"u"&&location.href||""),E=p?.[Ao]||"",m=void 0,b===E?m=p?.[Io]||"":E?m=E:m=document.referrer||"",[4,o.set(mn,(g={},g[Ao]=b,g[Io]=m,g))];case 2:y.sent(),y.label=3;case 3:return[2]}})})},h=function(){d()},v={name:"@amplitude/plugin-page-url-enrichment-browser",type:"enrichment",setup:function(p,b){return x(void 0,void 0,void 0,function(){var E;return O(this,function(m){switch(m.label){case 0:if(a=p.loggerProvider,a.log("Installing @amplitude/plugin-page-url-enrichment-browser"),c=!0,!i)return[3,2];try{o=new Hs(i.sessionStorage)}catch{a?.debug("sessionStorage is not available in this environment.")}return[4,o?.isEnabled()];case 1:s=(E=m.sent())!==null&&E!==void 0?E:!1,i.addEventListener("popstate",h),u||(i.history.pushState=new Proxy(i.history.pushState,{apply:function(g,y,_){var S=oe(_,3),A=S[0],I=S[1],C=S[2];g.apply(y,[A,I,C]),c&&h()}}),i.history.replaceState=new Proxy(i.history.replaceState,{apply:function(g,y,_){var S=oe(_,3),A=S[0],I=S[1],C=S[2];g.apply(y,[A,I,C]),c&&h()}}),u=!0),m.label=2;case 2:return[2]}})})},execute:function(p){return x(void 0,void 0,void 0,function(){var b,E,m,g,y;return O(this,function(_){switch(_.label){case 0:return b=xr(typeof location<"u"&&location.href||""),o&&s?[4,o.get(mn)]:[3,5];case 1:return E=_.sent(),E?.[Ao]?[3,3]:[4,o.set(mn,(y={},y[Ao]=b,y[Io]=document.referrer||"",y))];case 2:_.sent(),_.label=3;case 3:return[4,o.get(mn)];case 4:if(m=_.sent(),g="",m&&(g=m[Io]||""),fk.has(p.event_type))return[2,p];p.event_properties=D(D({},p.event_properties||{}),{"[Amplitude] Page Domain":hi(p,"[Amplitude] Page Domain",typeof location<"u"&&location.hostname||""),"[Amplitude] Page Location":hi(p,"[Amplitude] Page Location",b),"[Amplitude] Page Path":hi(p,"[Amplitude] Page Path",typeof location<"u"&&xr(location.pathname)||""),"[Amplitude] Page Title":hi(p,"[Amplitude] Page Title",Pl(Rl)),"[Amplitude] Page URL":hi(p,"[Amplitude] Page URL",b.split("?")[0]),"[Amplitude] Previous Page Location":g,"[Amplitude] Previous Page Type":f(g)}),_.label=5;case 5:return[2,p]}})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(p){switch(p.label){case 0:return i&&(i.removeEventListener("popstate",h),c=!1),o&&s?[4,o.set(mn,{})]:[3,2];case 1:p.sent(),p.label=2;case 2:return[2]}})})}};return v};function hi(e,t,r){return e.event_properties||(e.event_properties={}),e.event_properties[t]===void 0?r:e.event_properties[t]}var hk=function(){var e,t,r;function n(s){return typeof s!="object"||s===null?!1:"body"in s&&typeof s.body=="string"}function i(s){if(s)try{var a=new Function("return "+s)();if(typeof a=="function")return a;e?.error("Custom enrichment body did not evaluate to a function")}catch(u){e?.error("Could not create custom enrichment function",u)}return function(u){return u}}var o={name:"@amplitude/plugin-custom-enrichment-browser",type:"enrichment",setup:function(s,a){return x(void 0,void 0,void 0,function(){var u,c;return O(this,function(l){return e=s.loggerProvider,e?.log("Installing @amplitude/plugin-custom-enrichment-browser"),!((c=s.remoteConfig)===null||c===void 0)&&c.fetchRemoteConfig&&(s.remoteConfigClient?(u=s.remoteConfigClient.subscribe("configs.analyticsSDK.browserSDK.customEnrichment","all",function(f){f&&n(f)?r=i(f.body||""):r=i("")}),t=function(){var f;return(f=s.remoteConfigClient)===null||f===void 0?void 0:f.unsubscribe(u)}):e?.debug("Remote config client is not provided, skipping remote config fetch")),[2]})})},execute:function(s){return x(void 0,void 0,void 0,function(){var a;return O(this,function(u){if(r)try{return[2,(a=r(s))!==null&&a!==void 0?a:null]}catch(c){return e?.error("Could not execute custom enrichment function",c),[2,s]}return[2,s]})})},teardown:function(){return x(void 0,void 0,void 0,function(){return O(this,function(s){return t&&t(),[2]})})}};return o},Oa=-1,vk=(function(e){Mt(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r._diagnosticsSampleRate=0,r._enableRequestBodyCompressionExperimentalValue=!1,r}return t.prototype.init=function(r,n,i){r===void 0&&(r="");var o,s;return arguments.length>2?(o=n,s=i):typeof n=="string"?(o=n,s=void 0):(o=n?.userId,s=n),tt(this._init(D(D({},s),{userId:o,apiKey:r})))},t.prototype._init=function(r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h,v,p,b,E,m,g,y,_,S,A,I,C,R=this;return O(this,function(P){switch(P.label){case 0:return this.initializing?[2]:(this.initializing=!0,f=fI(r),d=(n=r.loggerProvider)!==null&&n!==void 0?n:new Kn,r.loggerProvider||d.enable((i=r.logLevel)!==null&&i!==void 0?i:ot.Warn),h=(o=r.serverZone)!==null&&o!==void 0?o:_g,p=this._diagnosticsSampleRate,b=(s=r.enableDiagnostics)!==null&&s!==void 0?s:!0,f?(v=new Xp(r.apiKey,d,h,(a=r.remoteConfig)===null||a===void 0?void 0:a.serverUrl),[4,new Promise(function(k){v?.subscribe("configs.diagnostics.browserSDK","all",function(N,$,ee){if(d.debug("Diagnostics remote configuration received:",JSON.stringify({remoteConfig:N,source:$,lastFetch:ee},null,2)),N){var B=N.sampleRate;typeof B=="number"&&!isNaN(B)&&(p=B);var z=N.enabled;typeof z=="boolean"&&(b=z)}k()})})]):[3,2]);case 1:P.sent(),P.label=2;case 2:return E=new UT(r.apiKey,d,h,{enabled:b,sampleRate:p}),E.setTag("library","".concat(bg,"/").concat(xl)),typeof navigator<"u"&&E.setTag("user_agent",navigator.userAgent),[4,lI(r.apiKey,r,this,E,{loggerProvider:d,serverZone:h,enableDiagnostics:b,diagnosticsSampleRate:p})];case 3:return m=P.sent(),f&&v?[4,new Promise(function(k){v?.subscribe("configs.analyticsSDK.browserSDK","all",function(N,$,ee){m.loggerProvider.debug("Remote configuration received:",JSON.stringify({remoteConfig:N,source:$,lastFetch:ee},null,2)),N&&SI(N,m),k()})})]:[3,5];case 4:P.sent(),P.label=5;case 5:return[4,e.prototype._init.call(this,m)];case 6:return P.sent(),this.logBrowserOptions(m),this.config.remoteConfigClient=v,hg(this.config.defaultTracking)?(this.config.optOut&&this.timeline.addOptOutListener(function(k){return x(R,void 0,void 0,function(){var N;return O(this,function($){switch($.label){case 0:return k?[3,2]:(N=Zf(this.config),this.webAttribution=new xd(N,this.config),[4,this.webAttribution.init()]);case 1:$.sent(),$.label=2;case 2:return[2]}})})}),g=Zf(this.config),this.webAttribution=new xd(g,this.config),[4,this.webAttribution.init()]):[3,8];case 7:P.sent(),P.label=8;case 8:return y=ps(),_=y.ampTimestamp?Number(y.ampTimestamp):void 0,S=_?Date.now()<_:!0,A=S&&!Number.isNaN(Number(y.ampSessionId))?Number(y.ampSessionId):void 0,I=this.config.deferredSessionId,I===Oa&&!this.config.optOut&&(I=Date.now()),this.setSessionId((l=(c=(u=r.sessionId)!==null&&u!==void 0?u:A)!==null&&c!==void 0?c:I)!==null&&l!==void 0?l:this.config.sessionId),this.config.optOut&&this.timeline.addOptOutListener(function(k){return x(R,void 0,void 0,function(){return O(this,function(N){return!k&&this.config.deferredSessionId&&(this.config.deferredSessionId===Oa?this.setSessionId(void 0):this.setSessionId(this.config.deferredSessionId)),[2]})})}),C=zn(r.instanceName),C.identityStore.setIdentity({userId:this.config.userId,deviceId:this.config.deviceId}),this.config.offline===Yp?[3,10]:[4,this.add(_I()).promise];case 9:P.sent(),P.label=10;case 10:return[4,this.add(new lT({diagnosticsClient:E})).promise];case 11:return P.sent(),[4,this.add(new FA).promise];case 12:return P.sent(),[4,this.add(new bT).promise];case 13:return P.sent(),EI(this.config),kA(this.config.defaultTracking)?(this.config.loggerProvider.debug("Adding file download tracking plugin"),[4,this.add(bI()).promise]):[3,15];case 14:P.sent(),P.label=15;case 15:return vg(this.config.defaultTracking)?(this.config.loggerProvider.debug("Adding form interaction plugin"),[4,this.add(yI()).promise]):[3,17];case 16:P.sent(),P.label=17;case 17:return pg(this.config.defaultTracking)?this.config.optOut?[3,19]:(this.config.loggerProvider.debug("Adding page view tracking plugin"),[4,this.add(od(Qf(this.config))).promise]):[3,20];case 18:return P.sent(),[3,20];case 19:this.timeline.addOptOutListener(function(k){return x(R,void 0,void 0,function(){return O(this,function(N){switch(N.label){case 0:return k?[2]:(this.config.loggerProvider.debug("Adding page view tracking plugin"),[4,this.add(od(Qf(this.config))).promise]);case 1:return N.sent(),[2]}})})}),P.label=20;case 20:return mg(this.config.autocapture)?(this.config.loggerProvider.debug("Adding user interactions plugin (autocapture plugin)"),[4,this.add(PC(xA(this.config),{diagnosticsClient:E})).promise]):[3,22];case 21:P.sent(),P.label=22;case 22:return yg(this.config.autocapture)?(this.config.loggerProvider.debug("Adding frustration interactions plugin"),[4,this.add($C(NA(this.config))).promise]):[3,24];case 23:P.sent(),P.label=24;case 24:return gg(this.config.autocapture)?(this.config.loggerProvider.debug("Adding network tracking plugin"),[4,this.add(JC(LA(this.config))).promise]):[3,26];case 25:P.sent(),P.label=26;case 26:return PA(this.config.autocapture)?(this.config.loggerProvider.debug("Adding web vitals plugin"),[4,this.add(ck()).promise]):[3,28];case 27:P.sent(),P.label=28;case 28:return RA(this.config.autocapture)?(this.config.loggerProvider.debug("Adding referrer page url plugin"),[4,this.add(dk()).promise]):[3,30];case 29:P.sent(),P.label=30;case 30:return OA(this.config.customEnrichment)?(this.config.loggerProvider.debug("Adding custom enrichment plugin"),[4,this.add(hk()).promise]):[3,32];case 31:P.sent(),P.label=32;case 32:return this.initializing=!1,[4,this.runQueuedFunctions("dispatchQ")];case 33:return P.sent(),C.eventBridge.setEventReceiver(function(k){var N=k.eventProperties||{},$=N.time,ee=hs(N,["time"]),B=typeof $=="number"?{time:$}:void 0;R.track(k.eventType,ee,B)}),[2]}})})},t.prototype.getUserId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.userId},t.prototype.setUserId=function(r){if(!this.config){this.q.push(this.setUserId.bind(this,r));return}this.config.loggerProvider.debug("function setUserId: ",r),(r!==this.config.userId||r===void 0)&&(this.config.userId=r,this.timeline.onIdentityChanged({userId:r}),mT(r,this.config.instanceName))},t.prototype.getDeviceId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.deviceId},t.prototype.setDeviceId=function(r){if(!this.config){this.q.push(this.setDeviceId.bind(this,r));return}this.config.loggerProvider.debug("function setDeviceId: ",r),r!==this.config.deviceId&&(this.config.deviceId=r,this.timeline.onIdentityChanged({deviceId:r}),yT(r,this.config.instanceName))},t.prototype.reset=function(){this.setDeviceId(Qt()),this.setUserId(void 0),this.timeline.onReset()},t.prototype.getIdentity=function(){var r,n;return{deviceId:(r=this.config)===null||r===void 0?void 0:r.deviceId,userId:(n=this.config)===null||n===void 0?void 0:n.userId,userProperties:this.userProperties}},t.prototype.setIdentity=function(r){var n,i,o;if("userId"in r&&this.setUserId(r.userId),"deviceId"in r&&r.deviceId&&this.setDeviceId(r.deviceId),"userProperties"in r){this.userProperties=r.userProperties;var s=new Dn,a=(o=r.userProperties)!==null&&o!==void 0?o:{};try{for(var u=ce(Object.entries(a)),c=u.next();!c.done;c=u.next()){var l=oe(c.value,2),f=l[0],d=l[1];s.set(f,d)}}catch(h){n={error:h}}finally{try{c&&!c.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}this.identify(s)}},t.prototype.getOptOut=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.optOut},t.prototype.getSessionId=function(){var r;return(r=this.config)===null||r===void 0?void 0:r.sessionId},t.prototype.setSessionId=function(r){var n,i=[];if(!this.config)return this.q.push(this.setSessionId.bind(this,r)),tt(Promise.resolve());if(this.config.optOut)return this.config.deferredSessionId=r??Oa,tt(Promise.resolve());if(r===void 0&&(r=Date.now()),r===this.config.sessionId)return tt(Promise.resolve());this.config.loggerProvider.debug("function setSessionId: ",r);var o=this.getSessionId();o!==r&&this.timeline.onSessionIdChanged(r);var s=this.config.lastEventTime,a=(n=this.config.lastEventId)!==null&&n!==void 0?n:-1;this.config.sessionId=r,this.config.lastEventTime=void 0,this.config.pageCounter=0,Jf(this.config.defaultTracking)&&(o&&s&&i.push(this.track(rd,void 0,{device_id:this.previousSessionDeviceId,event_id:++a,session_id:o,time:s+1,user_id:this.previousSessionUserId}).promise),this.config.lastEventTime=this.config.sessionId);var u=this.trackCampaignEventIfNeeded(++a,i);return this.config.identify&&i.push(this.track(Tl(this.config.identify)).promise),Jf(this.config.defaultTracking)&&i.push(this.track(td,void 0,{event_id:u?++a:a,session_id:this.config.sessionId,time:this.config.lastEventTime}).promise),this.previousSessionDeviceId=this.config.deviceId,this.previousSessionUserId=this.config.userId,tt(Promise.all(i))},t.prototype.extendSession=function(){if(!this.config){this.q.push(this.extendSession.bind(this));return}this.config.lastEventTime=Date.now()},t.prototype.setTransport=function(r){if(!this.config){this.q.push(this.setTransport.bind(this,r));return}this.config.transportProvider=Tg(r)},t.prototype.identify=function(r,n){if(Sa(r)){var i=r._q;r._q=[],r=_a(new Dn,i)}return n?.user_id&&this.setUserId(n.user_id),n?.device_id&&this.setDeviceId(n.device_id),e.prototype.identify.call(this,r,n)},t.prototype.groupIdentify=function(r,n,i,o){if(Sa(i)){var s=i._q;i._q=[],i=_a(new Dn,s)}return e.prototype.groupIdentify.call(this,r,n,i,o)},t.prototype.revenue=function(r,n){if(Sa(r)){var i=r._q;r._q=[],r=_a(new Hp,i)}return e.prototype.revenue.call(this,r,n)},t.prototype.trackCampaignEventIfNeeded=function(r,n){if(!this.webAttribution||!this.webAttribution.shouldTrackNewCampaign)return!1;var i=this.webAttribution.generateCampaignEvent(r);return n?n.push(this.track(i).promise):this.track(i),this.config.loggerProvider.log("Tracking attribution."),!0},t.prototype.process=function(r){return x(this,void 0,void 0,function(){var n,i,o;return O(this,function(s){return n=Date.now(),i=qp(this.config.sessionTimeout,this.config.lastEventTime),o=this.webAttribution&&this.webAttribution.shouldSetSessionIdOnNewCampaign(),r.event_type!==td&&r.event_type!==rd&&(!r.session_id||r.session_id===this.getSessionId())&&(i||o?(this.setSessionId(n),o&&this.config.loggerProvider.log("Created a new session for new campaign.")):i||this.trackCampaignEventIfNeeded()),[2,e.prototype.process.call(this,r)]})})},t.prototype.logBrowserOptions=function(r){try{var n=D(D({},r),{apiKey:r.apiKey.substring(0,10)+"********"});this.config.loggerProvider.debug("Initialized Amplitude with BrowserConfig:",EA(n))}catch(i){this.config.loggerProvider.error("Error logging browser config",i)}},t.prototype._setDiagnosticsSampleRate=function(r){if(!(r>1||r<0)&&!this.config){this._diagnosticsSampleRate=r;return}},t.prototype._enableRequestBodyCompressionExperimental=function(r){if(!this.config){this._enableRequestBodyCompressionExperimentalValue=r;return}},t})(tT),Qg="[Amplitude]",pk="".concat(Qg," Session Replay ID"),gk=0,mk=Xn.US,yk={enabled:!0},Fl=1e3,bk="".concat(Qg," Session Replay Debug"),Ek="amp-block",Zg="amp-mask",_k="amp-unmask",em="https://api-sr.amplitude.com/sessions/v2/track",tm="https://api-sr.eu.amplitude.com/sessions/v2/track",rm="https://api-sr.stag2.amplitude.com/sessions/v2/track",Sk=1*1e6,wk=3e4,Tk=6e4,Ak=500,Ik=10*1e3,nm=1024,Ck=1e3,kr;(function(e){e.GET_SR_PROPS="get-sr-props",e.DEBUG_INFO="debug-info",e.FETCH_REQUEST="fetch-request",e.METADATA="metadata",e.TARGETING_DECISION="targeting-decision"})(kr||(kr={}));var xu=(function(){function e(t){this.logger=t,this.log=this.getSafeMethod("log"),this.warn=this.getSafeMethod("warn"),this.error=this.getSafeMethod("error"),this.debug=this.getSafeMethod("debug")}return e.prototype.getSafeMethod=function(t){var r;if(!this.logger)return(function(){});var n=this.logger[t];if(typeof n=="function"){var i=(r=n.__rrweb_original__)!==null&&r!==void 0?r:n;return i.bind(this.logger)}return(function(){})},e.prototype.enable=function(t){this.logger.enable(t)},e.prototype.disable=function(){this.logger.disable()},e})(),Nu="medium";function kk(e){return e.toLowerCase()}function Rk(e){var t=e.type;return e.hasAttribute("data-rr-is-password")?"password":t?kk(t):null}var im=function(e,t,r){switch(t){case"light":{if(e!=="input")return!0;var n=r?Rk(r):"";return n?!!(["password","hidden","email","tel"].includes(n)||r.autocomplete.startsWith("cc-")):!1}case"medium":case"conservative":return!0;default:return im(e,Nu,r)}},om=function(e,t,r){var n,i,o;if(t===void 0&&(t={defaultMaskLevel:Nu}),r){if(r.closest("."+Zg))return!0;var s=((n=t.maskSelector)!==null&&n!==void 0?n:[]).some(function(u){return r.closest(u)});if(s)return!0;if(r.closest("."+_k))return!1;var a=((i=t.unmaskSelector)!==null&&i!==void 0?i:[]).some(function(u){return r.closest(u)});if(a)return!1}return im(e,(o=t.defaultMaskLevel)!==null&&o!==void 0?o:Nu,r)},Nd=function(e,t){return function(r,n){return om(e,t,n)?r.replace(/[^\s]/g,"*"):r}},Pk=function(e){return function(t,r,n){var i;return t==="style"||!((i=e?.maskAttributes)!==null&&i!==void 0?i:[]).includes(t)?r:om("text",e,n)?r.replace(/[^\s]/g,"*"):r}},Ok=function(){var e=le();return e?.location?e.location.href:""},xk=function(e,t){return"".concat(t,"/").concat(e)},Hl=function(e,t){return t||(e===Xn.STAGING?rm:e===Xn.EU?tm:em)},Nk=function(e){if(typeof e!="string"||e.trim()==="")return!1;var t=/^\/|^https?:\/\/[^\s]+$/;return!!t.test(e)},Lk=function(e){var t=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp("^".concat(t,"$"))},Mk=function(e){if(!e.every(function(t){return typeof t.selector=="string"&&typeof t.replacement=="string"}))throw new Error("ugcFilterRules must be an array of objects with selector and replacement properties");if(!e.every(function(t){return Nk(t.selector)}))throw new Error("ugcFilterRules must be an array of objects with valid globs")},Vs=function(e,t){var r,n;try{for(var i=ce(t),o=i.next();!o.done;o=i.next()){var s=o.value,a=Lk(s.selector);if(a.test(e))return e.replace(a,s.replacement)}}catch(u){r={error:u}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return e},sm=function(){return x(void 0,void 0,void 0,function(){var e,t,r,n,i,o,s;return O(this,function(a){switch(a.label){case 0:return a.trys.push([0,3,,4]),e=le(),e?[4,e.navigator.storage.estimate()]:[3,2];case 1:return t=a.sent(),r=t.usage,n=t.quota,i=t.usageDetails,o=r?Math.round(r/nm):0,s=r&&n?Math.round((r/n+Number.EPSILON)*1e3)/1e3:0,[2,{totalStorageSize:o,percentOfQuota:s,usageDetails:JSON.stringify(i)}];case 2:return[3,4];case 3:return a.sent(),[3,4];case 4:return[2,{totalStorageSize:0,percentOfQuota:0,usageDetails:""}]}})})},am=function(e){var t=D({},e),r=t.apiKey;return t.apiKey="****".concat(r.substring(r.length-4)),t},um=function(){return{flushMaxRetries:2,logLevel:ot.Warn,loggerProvider:new Kn,transportProvider:new FT}},Dk=(function(e){Mt(t,e);function t(r,n){var i=this,o,s,a,u,c,l,f=um();if(i=e.call(this,D(D({transportProvider:f.transportProvider,loggerProvider:new xu(n.loggerProvider||f.loggerProvider)},n),{apiKey:r}))||this,i.flushMaxRetries=n.flushMaxRetries!==void 0&&n.flushMaxRetries<=f.flushMaxRetries?n.flushMaxRetries:f.flushMaxRetries,i.apiKey=r,i.sampleRate=n.sampleRate||gk,i.serverZone=n.serverZone||mk,i.configServerUrl=n.configServerUrl,i.trackServerUrl=n.trackServerUrl,i.shouldInlineStylesheet=n.shouldInlineStylesheet,i.version=n.version,i.performanceConfig=n.performanceConfig||yk,i.storeType=(o=n.storeType)!==null&&o!==void 0?o:"idb",i.applyBackgroundColorToBlockedElements=(s=n.applyBackgroundColorToBlockedElements)!==null&&s!==void 0?s:!1,i.enableUrlChangePolling=(a=n.enableUrlChangePolling)!==null&&a!==void 0?a:!1,i.urlChangePollingInterval=(u=n.urlChangePollingInterval)!==null&&u!==void 0?u:Fl,i.captureDocumentTitle=(c=n.captureDocumentTitle)!==null&&c!==void 0?c:!1,n.privacyConfig&&(i.privacyConfig=n.privacyConfig),n.interactionConfig&&(i.interactionConfig=n.interactionConfig,i.interactionConfig.ugcFilterRules&&Mk(i.interactionConfig.ugcFilterRules)),n.debugMode&&(i.debugMode=n.debugMode),n.useWebWorker!==void 0)i.useWebWorker=n.useWebWorker;else{var d=n;((l=d.experimental)===null||l===void 0?void 0:l.useWebWorker)!==void 0&&(i.useWebWorker=d.experimental.useWebWorker)}return n.omitElementTags&&(i.omitElementTags=n.omitElementTags),i}return t})(Bp),lm=(e=>(e[e.DomContentLoaded=0]="DomContentLoaded",e[e.Load=1]="Load",e[e.FullSnapshot=2]="FullSnapshot",e[e.IncrementalSnapshot=3]="IncrementalSnapshot",e[e.Meta=4]="Meta",e[e.Custom=5]="Custom",e[e.Plugin=6]="Plugin",e))(lm||{}),cm=(e=>(e[e.MouseUp=0]="MouseUp",e[e.MouseDown=1]="MouseDown",e[e.Click=2]="Click",e[e.ContextMenu=3]="ContextMenu",e[e.DblClick=4]="DblClick",e[e.Focus=5]="Focus",e[e.Blur=6]="Blur",e[e.TouchStart=7]="TouchStart",e[e.TouchMove_Departed=8]="TouchMove_Departed",e[e.TouchEnd=9]="TouchEnd",e[e.TouchCancel=10]="TouchCancel",e))(cm||{}),Uk=function(e,t){var r=document.createDocumentFragment(),n=function(i){if(i===void 0&&(i=[]),typeof i=="string"&&(i=[i]),i=i.filter(function(o){try{r.querySelector(o)}catch{return t.warn('[session-replay-browser] omitting selector "'.concat(o,'" because it is invalid')),!1}return!0}),i.length!==0)return i};return e.blockSelector=n(e.blockSelector),e.maskSelector=n(e.maskSelector),e.unmaskSelector=n(e.unmaskSelector),e},Fk=(function(){function e(t,r){this.localConfig=r,this.remoteConfigClient=t}return e.prototype.generateJoinedConfig=function(){var t,r,n,i,o;return x(this,void 0,void 0,function(){var s,a,u,c,l,f,d,h,v,p,b,E,m,g,y,_,S,A=this;return O(this,function(I){switch(I.label){case 0:s=D({},this.localConfig),s.optOut=this.localConfig.optOut,s.captureEnabled=!0,I.label=1;case 1:return I.trys.push([1,3,,4]),[4,new Promise(function(C,R){A.remoteConfigClient.subscribe("configs.sessionReplay","all",function(P,k){var N;if(A.localConfig.loggerProvider.debug("Session Replay remote configuration received from ".concat(k,":"),JSON.stringify(P,null,2)),!P){R(new Error("No remote config received"));return}var $=P,ee=$.sr_sampling_config,B=$.sr_privacy_config,z=$.sr_targeting_config,K=(N=s.interactionConfig)===null||N===void 0?void 0:N.ugcFilterRules;s.interactionConfig=$.sr_interaction_config,s.interactionConfig&&K&&(s.interactionConfig.ugcFilterRules=K),s.loggingConfig=$.sr_logging_config,(ee||B||z)&&(a={},ee&&(a.sr_sampling_config=ee),B&&(a.sr_privacy_config=B),z&&(a.sr_targeting_config=z)),C()})})];case 2:return I.sent(),[3,4];case 3:return u=I.sent(),this.localConfig.loggerProvider.error("Failed to generate joined config: ",u),s.captureEnabled=!1,[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:void 0}];case 4:if(!a)return[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:a}];if(c=a.sr_sampling_config,l=a.sr_privacy_config,f=a.sr_targeting_config,c&&Object.keys(c).length>0?(Object.prototype.hasOwnProperty.call(c,"capture_enabled")?s.captureEnabled=c.capture_enabled:s.captureEnabled=!1,Object.prototype.hasOwnProperty.call(c,"sample_rate")&&(s.sampleRate=c.sample_rate)):(s.captureEnabled=!0,this.localConfig.loggerProvider.debug("Remote config successfully fetched, but no values set for project, Session Replay capture enabled.")),l){d=(t=s.privacyConfig)!==null&&t!==void 0?t:{},h={defaultMaskLevel:(n=(r=l.defaultMaskLevel)!==null&&r!==void 0?r:d.defaultMaskLevel)!==null&&n!==void 0?n:"medium",blockSelector:[],maskSelector:[],unmaskSelector:[],maskAttributes:Ee([],oe(new Set(Ee(Ee([],oe((i=d.maskAttributes)!==null&&i!==void 0?i:[]),!1),oe((o=l.maskAttributes)!==null&&o!==void 0?o:[]),!1))),!1)},v=function(C){var R,P,k,N,$,ee,B,z,K,Q={};typeof C.blockSelector=="string"&&(C.blockSelector=[C.blockSelector]);try{for(var ae=ce((B=C.blockSelector)!==null&&B!==void 0?B:[]),fe=ae.next();!fe.done;fe=ae.next()){var pe=fe.value;Q[pe]="block"}}catch(M){R={error:M}}finally{try{fe&&!fe.done&&(P=ae.return)&&P.call(ae)}finally{if(R)throw R.error}}try{for(var ye=ce((z=C.maskSelector)!==null&&z!==void 0?z:[]),be=ye.next();!be.done;be=ye.next()){var pe=be.value;Q[pe]="mask"}}catch(M){k={error:M}}finally{try{be&&!be.done&&(N=ye.return)&&N.call(ye)}finally{if(k)throw k.error}}try{for(var de=ce((K=C.unmaskSelector)!==null&&K!==void 0?K:[]),me=de.next();!me.done;me=de.next()){var pe=me.value;Q[pe]="unmask"}}catch(M){$={error:M}}finally{try{me&&!me.done&&(ee=de.return)&&ee.call(de)}finally{if($)throw $.error}}return Q},p=D(D({},v(d)),v(l));try{for(b=ce(Object.entries(p)),E=b.next();!E.done;E=b.next())m=oe(E.value,2),g=m[0],y=m[1],y==="mask"?h.maskSelector.push(g):y==="block"?h.blockSelector.push(g):y==="unmask"&&h.unmaskSelector.push(g)}catch(C){_={error:C}}finally{try{E&&!E.done&&(S=b.return)&&S.call(b)}finally{if(_)throw _.error}}s.privacyConfig=Uk(h,this.localConfig.loggerProvider)}return f&&Object.keys(f).length>0&&(s.targetingConfig=f),this.localConfig.loggerProvider.debug(JSON.stringify({name:"session replay joined config",config:am(s)},null,2)),[2,{localConfig:this.localConfig,joinedConfig:s,remoteConfig:a}]}})})},e})(),Hk=function(e,t){return x(void 0,void 0,void 0,function(){var r,n;return O(this,function(i){return r=new Dk(e,t),n=new Xp(e,r.loggerProvider,r.serverZone,t.configServerUrl),[2,new Fk(n,r)]})})},xt=Uint8Array,pt=Uint16Array,Xi=Uint32Array,Bl=new xt([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),jl=new xt([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Ld=new xt([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),fm=function(e,t){for(var r=new pt(31),n=0;n<31;++n)r[n]=t+=1<>>1|(Ue&21845)<<1;Sr=(Sr&52428)>>>2|(Sr&13107)<<2,Sr=(Sr&61680)>>>4|(Sr&3855)<<4,Mu[Ue]=((Sr&65280)>>>8|(Sr&255)<<8)>>>1}var Mi=(function(e,t,r){for(var n=e.length,i=0,o=new pt(t);i>>u]=c}else for(a=new pt(n),i=0;i>>15-e[i];return a}),ln=new xt(288);for(var Ue=0;Ue<144;++Ue)ln[Ue]=8;for(var Ue=144;Ue<256;++Ue)ln[Ue]=9;for(var Ue=256;Ue<280;++Ue)ln[Ue]=7;for(var Ue=280;Ue<288;++Ue)ln[Ue]=8;var Es=new xt(32);for(var Ue=0;Ue<32;++Ue)Es[Ue]=5;var qk=Mi(ln,9,0),Vk=Mi(Es,5,0),hm=function(e){return(e/8>>0)+(e&7&&1)},vm=function(e,t,r){(r==null||r>e.length)&&(r=e.length);var n=new(e instanceof pt?pt:e instanceof Xi?Xi:xt)(r-t);return n.set(e.subarray(t,r)),n},or=function(e,t,r){r<<=t&7;var n=t/8>>0;e[n]|=r,e[n+1]|=r>>>8},vi=function(e,t,r){r<<=t&7;var n=t/8>>0;e[n]|=r,e[n+1]|=r>>>8,e[n+2]|=r>>>16},xa=function(e,t){for(var r=[],n=0;nd&&(d=o[n].s);var h=new pt(d+1),v=Du(r[l-1],h,0);if(v>t){var n=0,p=0,b=v-t,E=1<t)p+=E-(1<>>=b;p>0;){var g=o[n].s;h[g]=0&&p;--n){var y=o[n].s;h[y]==t&&(--h[y],++p)}v=t}return[new xt(h),v]},Du=function(e,t,r){return e.s==-1?Math.max(Du(e.l,t,r+1),Du(e.r,t,r+1)):t[e.s]=r},Dd=function(e){for(var t=e.length;t&&!e[--t];);for(var r=new pt(++t),n=0,i=e[0],o=1,s=function(u){r[n++]=u},a=1;a<=t;++a)if(e[a]==i&&a!=t)++o;else{if(!i&&o>2){for(;o>138;o-=138)s(32754);o>2&&(s(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(s(i),--o;o>6;o-=6)s(8304);o>2&&(s(o-3<<5|8208),o=0)}for(;o--;)s(i);o=1,i=e[a]}return[r.subarray(0,n),t]},pi=function(e,t){for(var r=0,n=0;n>>8,e[i+2]=e[i]^255,e[i+3]=e[i+1]^255;for(var o=0;o4&&!R[Ld[k-1]];--k);var N=c+5<<3,$=pi(i,ln)+pi(o,Es)+s,ee=pi(i,d)+pi(o,p)+s+14+3*k+pi(A,R)+(2*A[16]+3*A[17]+7*A[18]);if(N<=$&&N<=ee)return Uu(t,l,e.subarray(u,u+c));var B,z,K,Q;if(or(t,l,1+(ee<$)),l+=2,ee<$){B=Mi(d,h,0),z=d,K=Mi(p,b,0),Q=p;var ae=Mi(R,P,0);or(t,l,g-257),or(t,l+5,S-1),or(t,l+10,k-4),l+=14;for(var I=0;I15&&(or(t,l,ye[I]>>>5&127),l+=ye[I]>>>12)}}else B=qk,z=ln,K=Vk,Q=Es;for(var I=0;I255){var be=n[I]>>>18&31;vi(t,l,B[be+257]),l+=z[be+257],be>7&&(or(t,l,n[I]>>>23&31),l+=Bl[be]);var de=n[I]&31;vi(t,l,K[de]),l+=Q[de],de>3&&(vi(t,l,n[I]>>>5&8191),l+=jl[de])}else vi(t,l,B[n[I]]),l+=z[n[I]];return vi(t,l,B[256]),l+z[256]},$k=new Xi([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),Wk=function(e,t,r,n,i,o){var s=e.length,a=new xt(n+s+5*(1+Math.floor(s/7e3))+i),u=a.subarray(n,a.length-i),c=0;if(!t||s<8)for(var l=0;l<=s;l+=65535){var f=l+65535;f>>13,v=d&8191,p=(1<7e3||R>24576)&&B>423){c=Ud(e,u,0,_,S,A,C,R,k,l-k,c),R=I=C=0,k=l;for(var z=0;z<286;++z)S[z]=0;for(var z=0;z<30;++z)A[z]=0}var K=2,Q=0,ae=v,fe=$-ee&32767;if(B>2&&N==y(l-fe))for(var pe=Math.min(h,B)-1,ye=Math.min(32767,l),be=Math.min(258,B);fe<=ye&&--ae&&$!=ee;){if(e[l+K]==e[l+K-fe]){for(var de=0;deK){if(K=de,Q=fe,de>pe)break;for(var me=Math.min(fe,de-2),M=0,z=0;zM&&(M=re,ee=Z)}}}$=ee,ee=b[$],fe+=$-ee+32768&32767}if(Q){_[R++]=268435456|Lu[K]<<18|Md[Q];var F=Lu[K]&31,ge=Md[Q]&31;C+=Bl[F]+jl[ge],++S[257+F],++A[ge],P=l+K,++I}else _[R++]=e[l],++S[e[l]]}}c=Ud(e,u,o,_,S,A,C,R,k,l-k,c)}return vm(a,0,n+hm(c)+i)},Gk=function(){var e=1,t=0;return{p:function(r){for(var n=e,i=t,o=r.length,s=0;s!=o;){for(var a=Math.min(s+5552,o);s>>8<<16|(t&255)<<8|t>>>8)+((e&255)<<23)*2}}},Kk=function(e,t,r,n,i){return Wk(e,t.level==null?6:t.level,t.mem==null?Math.ceil(Math.max(8,Math.min(13,Math.log(e.length)))*1.5):12+t.mem,r,n,!0)},zk=function(e,t,r){for(;r;++t)e[t]=r,r>>>=8},Xk=function(e,t){var r=t.level,n=r==0?0:r<6?1:r==9?3:2;e[0]=120,e[1]=n<<6|(n?32-2*n:1)};function Yk(e,t){t===void 0&&(t={});var r=Gk();r.p(e);var n=Kk(e,t,2,4);return Xk(n,t),zk(n,n.length-4,r.d()),n}function Jk(e,t){var r=e.length;if(typeof TextEncoder<"u")return new TextEncoder().encode(e);for(var n=new xt(e.length+(e.length>>>1)),i=0,o=function(c){n[i++]=c},s=0;sn.length){var a=new xt(i+8+(r-s<<1));a.set(n),n=a}var u=e.charCodeAt(s);u<128||t?o(u):u<2048?(o(192|u>>>6),o(128|u&63)):u>55295&&u<57344?(u=65536+(u&1047552)|e.charCodeAt(++s)&1023,o(240|u>>>18),o(128|u>>>12&63),o(128|u>>>6&63),o(128|u&63)):(o(224|u>>>12),o(128|u>>>6&63),o(128|u&63))}return vm(n,0,i)}function Qk(e,t){for(var r="",n=0;n{const t={...e,v:Zk};return Qk(Yk(Jk(JSON.stringify(t))))};var tR=2e3,rR=(function(){function e(t,r,n,i){var o=this,s;this.taskQueue=[],this.isProcessing=!1,this.compressEvent=function(f){var d=eR(f);return JSON.stringify(d)},this.addCompressedEventToManager=function(f,d){o.eventsManager&&o.deviceId&&o.eventsManager.addEvent({event:{type:"replay",data:f},sessionId:d,deviceId:o.deviceId})},this.addCompressedEvent=function(f,d){if(o.worker)try{o.worker.postMessage({event:f,sessionId:d})}catch(v){v.name==="DataCloneError"?o.worker.postMessage(JSON.stringify({event:f,sessionId:d})):o.config.loggerProvider.warn("Unexpected error while posting message to worker:",v)}else{var h=o.compressEvent(f);o.addCompressedEventToManager(h,d)}},this.terminate=function(){var f;(f=o.worker)===null||f===void 0||f.terminate()};var a=le();if(this.canUseIdleCallback=a&&"requestIdleCallback"in a,this.eventsManager=t,this.config=r,this.deviceId=n,this.timeout=((s=r.performanceConfig)===null||s===void 0?void 0:s.timeout)||tR,i){r.loggerProvider.log("Enabling web worker for compression");try{var u=new Blob([i],{type:"application/javascript"}),c=URL.createObjectURL(u),l=new Worker(c);l.onerror=function(f){f.preventDefault(),r.loggerProvider.error("Worker failed, falling back to non-worker compression:",f),l.terminate(),o.worker=void 0},l.onmessage=function(f){var d=f.data,h=d.compressedEvent,v=d.sessionId;o.addCompressedEventToManager(h,v)},this.worker=l}catch(f){r.loggerProvider.error("Failed to create worker, falling back to non-worker compression:",f)}}}return e.prototype.scheduleIdleProcessing=function(){var t=this;this.isProcessing||(this.isProcessing=!0,requestIdleCallback(function(r){t.processQueue(r)},{timeout:this.timeout}))},e.prototype.enqueueEvent=function(t,r){var n;this.canUseIdleCallback&&(!((n=this.config.performanceConfig)===null||n===void 0)&&n.enabled)?(this.config.loggerProvider.debug("Enqueuing event for processing during idle time."),this.taskQueue.push({event:t,sessionId:r}),this.scheduleIdleProcessing()):(this.config.loggerProvider.debug("Processing event without idle callback."),this.addCompressedEvent(t,r))},e.prototype.processQueue=function(t){for(var r=this;this.taskQueue.length>0&&(t.timeRemaining()>0||t.didTimeout);){var n=this.taskQueue.shift();if(n){var i=n.event,o=n.sessionId;this.addCompressedEvent(i,o)}}this.taskQueue.length>0?requestIdleCallback(function(s){r.processQueue(s)},{timeout:this.timeout}):this.isProcessing=!1},e})(),nR="Unexpected error occurred",iR="Network error occurred, event batch rejected",Fd="Session replay event batch rejected due to exceeded retry count",yn="Failed to store session replay events in IndexedDB",oR="Session replay event batch not sent due to missing device ID",sR="Session replay event batch not sent due to missing api key",Fu="1.35.1",aR=(function(){function e(t){var r=t.trackServerUrl,n=t.loggerProvider,i=t.payloadBatcher;this.storageKey="",this.retryTimeout=1e3,this.scheduled=null,this.queue=[],this.loggerProvider=n,this.payloadBatcher=i||function(o){return o},this.trackServerUrl=r}return e.prototype.sendEventsList=function(t){this.addToQueue(D(D({},t),{attempts:0,timeout:0}))},e.prototype.addToQueue=function(){for(var t=this,r=[],n=0;n0&&r.schedule(t)})},t))},e.prototype.flush=function(t){return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var r,n,i,o,s,a,u;return O(this,function(c){switch(c.label){case 0:r=this.queue,this.queue=[],this.scheduled&&(clearTimeout(this.scheduled),this.scheduled=null),c.label=1;case 1:c.trys.push([1,6,7,8]),n=ce(r),i=n.next(),c.label=2;case 2:return i.done?[3,5]:(o=i.value,[4,this.send(o,t)]);case 3:c.sent(),c.label=4;case 4:return i=n.next(),[3,2];case 5:return[3,8];case 6:return s=c.sent(),a={error:s},[3,8];case 7:try{i&&!i.done&&(u=n.return)&&u.call(n)}finally{if(a)throw a.error}return[7];case 8:return[2]}})})},e.prototype.send=function(t,r){var n,i;return r===void 0&&(r=!0),x(this,void 0,void 0,function(){var o,s,a,u,c,l,f,d,h,v,p,b,E;return O(this,function(m){switch(m.label){case 0:if(o=t.apiKey,!o)return[2,this.completeRequest({context:t,err:sR})];if(s=t.deviceId,!s)return[2,this.completeRequest({context:t,err:oR})];if(a=Ok(),u=Fu,c=t.sampleRate,l=new URLSearchParams({device_id:s,session_id:"".concat(t.sessionId),type:"".concat(t.type)}),f="".concat(((n=t.version)===null||n===void 0?void 0:n.type)||"standalone","/").concat(((i=t.version)===null||i===void 0?void 0:i.version)||u),d=this.payloadBatcher({version:1,events:t.events}),d.events.length===0)return this.completeRequest({context:t}),[2];m.label=1;case 1:return m.trys.push([1,6,,7]),h={headers:{"Content-Type":"application/json",Accept:"*/*",Authorization:"Bearer ".concat(o),"X-Client-Version":u,"X-Client-Library":f,"X-Client-Url":a.substring(0,Ck),"X-Client-Sample-Rate":"".concat(c),"X-Sampling-Hash-Alg":"xxhash32"},body:JSON.stringify(d),method:"POST"},v="".concat(Hl(t.serverZone,this.trackServerUrl),"?").concat(l.toString()),[4,fetch(v,h)];case 2:if(p=m.sent(),p===null)return this.completeRequest({context:t,err:nR}),[2];if(r)return[3,3];b="";try{b=JSON.stringify(p.body,null,2)}catch{}return this.completeRequest({context:t,success:"".concat(p.status,": ").concat(b)}),[3,5];case 3:return[4,this.handleReponse(p.status,t)];case 4:m.sent(),m.label=5;case 5:return[3,7];case 6:return E=m.sent(),this.completeRequest({context:t,err:E}),[3,7];case 7:return[2]}})})},e.prototype.handleReponse=function(t,r){return x(this,void 0,void 0,function(){var n,i;return O(this,function(o){switch(o.label){case 0:switch(n=new ro().buildStatus(t),i=n,i){case je.Success:return[3,1];case je.Failed:return[3,2]}return[3,4];case 1:return this.handleSuccessResponse(r),[3,5];case 2:return[4,this.handleOtherResponse(r)];case 3:return o.sent(),[3,5];case 4:this.completeRequest({context:r,err:iR}),o.label=5;case 5:return[2]}})})},e.prototype.handleSuccessResponse=function(t){var r=Math.round(new Blob(t.events).size/nm);this.completeRequest({context:t,success:"Session replay event batch tracked successfully for session id ".concat(t.sessionId,", size of events: ").concat(r," KB")})},e.prototype.handleOtherResponse=function(t){return x(this,void 0,void 0,function(){var r;return O(this,function(n){switch(n.label){case 0:return r=t.attempts*this.retryTimeout,t.attempts++,t.attempts>(t.flushMaxRetries||0)?(this.completeRequest({context:t,err:Fd}),[2]):[4,new Promise(function(i){return setTimeout(i,r)})];case 1:return n.sent(),[4,this.send(t,!0)];case 2:return n.sent(),[2]}})})},e.prototype.completeRequest=function(t){var r=t.context,n=t.err,i=t.success;r.onComplete(),n?this.loggerProvider.warn(n):i&&this.loggerProvider.log(i)},e})();const Hu=(e,t)=>t.some(r=>e instanceof r);let Hd,Bd;function uR(){return Hd||(Hd=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function lR(){return Bd||(Bd=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const Bu=new WeakMap,Na=new WeakMap,$s=new WeakMap;function cR(e){const t=new Promise((r,n)=>{const i=()=>{e.removeEventListener("success",o),e.removeEventListener("error",s)},o=()=>{r(an(e.result)),i()},s=()=>{n(e.error),i()};e.addEventListener("success",o),e.addEventListener("error",s)});return $s.set(t,e),t}function fR(e){if(Bu.has(e))return;const t=new Promise((r,n)=>{const i=()=>{e.removeEventListener("complete",o),e.removeEventListener("error",s),e.removeEventListener("abort",s)},o=()=>{r(),i()},s=()=>{n(e.error||new DOMException("AbortError","AbortError")),i()};e.addEventListener("complete",o),e.addEventListener("error",s),e.addEventListener("abort",s)});Bu.set(e,t)}let ju={get(e,t,r){if(e instanceof IDBTransaction){if(t==="done")return Bu.get(e);if(t==="store")return r.objectStoreNames[1]?void 0:r.objectStore(r.objectStoreNames[0])}return an(e[t])},set(e,t,r){return e[t]=r,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function pm(e){ju=e(ju)}function dR(e){return lR().includes(e)?function(...t){return e.apply(qu(this),t),an(this.request)}:function(...t){return an(e.apply(qu(this),t))}}function hR(e){return typeof e=="function"?dR(e):(e instanceof IDBTransaction&&fR(e),Hu(e,uR())?new Proxy(e,ju):e)}function an(e){if(e instanceof IDBRequest)return cR(e);if(Na.has(e))return Na.get(e);const t=hR(e);return t!==e&&(Na.set(e,t),$s.set(t,e)),t}const qu=e=>$s.get(e);function gm(e,t,{blocked:r,upgrade:n,blocking:i,terminated:o}={}){const s=indexedDB.open(e,t),a=an(s);return n&&s.addEventListener("upgradeneeded",u=>{n(an(s.result),u.oldVersion,u.newVersion,an(s.transaction),u)}),r&&s.addEventListener("blocked",u=>r(u.oldVersion,u.newVersion,u)),a.then(u=>{o&&u.addEventListener("close",()=>o()),i&&u.addEventListener("versionchange",c=>i(c.oldVersion,c.newVersion,c))}).catch(()=>{}),a}const vR=["get","getKey","getAll","getAllKeys","count"],pR=["put","add","delete","clear"],La=new Map;function jd(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(La.get(t))return La.get(t);const r=t.replace(/FromIndex$/,""),n=t!==r,i=pR.includes(r);if(!(r in(n?IDBIndex:IDBObjectStore).prototype)||!(i||vR.includes(r)))return;const o=async function(s,...a){const u=this.transaction(s,i?"readwrite":"readonly");let c=u.store;return n&&(c=c.index(a.shift())),(await Promise.all([c[r](...a),i&&u.done]))[0]};return La.set(t,o),o}pm(e=>({...e,get:(t,r,n)=>jd(t,r)||e.get(t,r,n),has:(t,r)=>!!jd(t,r)||e.has(t,r)}));const gR=["continue","continuePrimaryKey","advance"],qd={},Vu=new WeakMap,mm=new WeakMap,mR={get(e,t){if(!gR.includes(t))return e[t];let r=qd[t];return r||(r=qd[t]=function(...n){Vu.set(this,mm.get(this)[t](...n))}),r}};async function*yR(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const r=new Proxy(t,mR);for(mm.set(r,t),$s.set(r,qu(t));t;)yield r,t=await(Vu.get(r)||t.continue()),Vu.delete(r)}function Vd(e,t){return t===Symbol.asyncIterator&&Hu(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&Hu(e,[IDBIndex,IDBObjectStore])}pm(e=>({...e,get(t,r,n){return Vd(t,r)?yR:e.get(t,r,n)},has(t,r){return Vd(t,r)||e.has(t,r)}}));var ym=(function(){function e(t){var r=this,n,i,o;this.minInterval=Ak,this.maxInterval=Ik,this.maxPersistedEventsSize=Sk,this.interval=this.minInterval,this._timeAtLastSplit=Date.now(),this.shouldSplitEventsList=function(s,a){var u=r.getStringSize(a),c=r.getEventsArraySize(s);return c+u>=r.maxPersistedEventsSize?!0:Date.now()-r.timeAtLastSplit>r.interval&&s.length?(r.interval=Math.min(r.maxInterval,r.interval+r.minInterval),r._timeAtLastSplit=Date.now(),!0):!1},this.loggerProvider=t.loggerProvider,this.minInterval=(n=t.minInterval)!==null&&n!==void 0?n:this.minInterval,this.maxInterval=(i=t.maxInterval)!==null&&i!==void 0?i:this.maxInterval,this.maxPersistedEventsSize=(o=t.maxPersistedEventsSize)!==null&&o!==void 0?o:this.maxPersistedEventsSize}return Object.defineProperty(e.prototype,"timeAtLastSplit",{get:function(){return this._timeAtLastSplit},enumerable:!1,configurable:!0}),e.prototype.getStringSize=function(t){return t.length},e.prototype.getEventsArraySize=function(t){var r,n,i=0;try{for(var o=ce(t),s=o.next();!s.done;s=o.next()){var a=s.value;i+=this.getStringSize(a)}}catch(c){r={error:c}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}var u=2+Math.max(0,t.length-1)+t.length*2;return i+u},e})(),bR=function(e){return typeof e=="object"&&e!==null&&e.name==="AbortError"},ur=function(e,t,r){bR(r)?e.debug(t):e.warn(t)},Di="sessionCurrentSequence",Ui="sequencesToSend",ER=function(e){var t,r;return e.objectStoreNames.contains(Di)||(r=e.createObjectStore(Di,{keyPath:"sessionId"})),e.objectStoreNames.contains(Ui)||(t=e.createObjectStore(Ui,{keyPath:"sequenceId",autoIncrement:!0}),t.createIndex("sessionId","sessionId")),{sequencesStore:t,currentSequenceStore:r}},_R=function(e){return x(void 0,void 0,void 0,function(){return O(this,function(t){switch(t.label){case 0:return[4,gm(e,1,{upgrade:ER})];case 1:return[2,t.sent()]}})})},SR=(function(e){Mt(t,e);function t(r){var n=e.call(this,r)||this;return n.getSequencesToSend=function(){return x(n,void 0,void 0,function(){var i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,5,,6]),i=[],[4,this.db.transaction("sequencesToSend").store.openCursor()];case 1:o=l.sent(),l.label=2;case 2:return o?(s=o.value,a=s.sessionId,u=s.events,i.push({events:u,sequenceId:o.key,sessionId:a}),[4,o.continue()]):[3,4];case 3:return o=l.sent(),[3,2];case 4:return[2,i];case 5:return c=l.sent(),ur(this.loggerProvider,"".concat(yn,": ").concat(c),c),[3,6];case 6:return[2,void 0]}})})},n.storeCurrentSequence=function(i){return x(n,void 0,void 0,function(){var o,s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,4,,5]),[4,this.db.get(Di,i)];case 1:return o=u.sent(),o?[4,this.db.put(Ui,{sessionId:i,events:o.events})]:[2,void 0];case 2:return s=u.sent(),[4,this.db.put(Di,{sessionId:i,events:[]})];case 3:return u.sent(),[2,D(D({},o),{sessionId:i,sequenceId:s})];case 4:return a=u.sent(),ur(this.loggerProvider,"".concat(yn,": ").concat(a),a),[3,5];case 5:return[2,void 0]}})})},n.addEventToCurrentSequence=function(i,o){return x(n,void 0,void 0,function(){var s,a,u,c,l,f;return O(this,function(d){switch(d.label){case 0:return d.trys.push([0,10,,11]),s=this.db.transaction(Di,"readwrite"),[4,s.store.get(i)];case 1:return a=d.sent(),a?[3,3]:[4,s.store.put({sessionId:i,events:[o]})];case 2:return d.sent(),[2];case 3:return u=void 0,this.shouldSplitEventsList(a.events,o)?(u=a.events,[4,s.store.put({sessionId:i,events:[o]})]):[3,5];case 4:return d.sent(),[3,7];case 5:return c=a.events.concat(o),[4,s.store.put({sessionId:i,events:c})];case 6:d.sent(),d.label=7;case 7:return[4,s.done];case 8:return d.sent(),u?[4,this.storeSendingEvents(i,u)]:[2,void 0];case 9:return l=d.sent(),l?[2,{events:u,sessionId:i,sequenceId:l}]:[2,void 0];case 10:return f=d.sent(),ur(this.loggerProvider,"".concat(yn,": ").concat(f),f),[3,11];case 11:return[2,void 0]}})})},n.storeSendingEvents=function(i,o){return x(n,void 0,void 0,function(){var s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,2,,3]),[4,this.db.put(Ui,{sessionId:i,events:o})];case 1:return s=u.sent(),[2,s];case 2:return a=u.sent(),ur(this.loggerProvider,"".concat(yn,": ").concat(a),a),[3,3];case 3:return[2,void 0]}})})},n.cleanUpSessionEventsStore=function(i,o){return x(n,void 0,void 0,function(){var s;return O(this,function(a){switch(a.label){case 0:if(!o)return[2];a.label=1;case 1:return a.trys.push([1,3,,4]),[4,this.db.delete(Ui,o)];case 2:return a.sent(),[3,4];case 3:return s=a.sent(),ur(this.loggerProvider,"".concat(yn,": ").concat(s),s),[3,4];case 4:return[2]}})})},n.db=r.db,n}return t.new=function(r,n){return x(this,void 0,void 0,function(){var i,o,s,a;return O(this,function(u){switch(u.label){case 0:return u.trys.push([0,2,,3]),i=r==="replay"?"":"_".concat(r),o="".concat(n.apiKey.substring(0,10),"_amp_session_replay_events").concat(i),[4,_R(o)];case 1:return s=u.sent(),[2,new t(D(D({},n),{db:s}))];case 2:return a=u.sent(),ur(n.loggerProvider,"".concat(yn,": ").concat(a),a),[3,3];case 3:return[2]}})})},t.prototype.getCurrentSequenceEvents=function(r){return x(this,void 0,void 0,function(){var s,n,i,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return r?[4,this.db.get("sessionCurrentSequence",r)]:[3,2];case 1:return s=l.sent(),s?[2,[s]]:[2,void 0];case 2:n=[],l.label=3;case 3:return l.trys.push([3,8,9,10]),[4,this.db.getAll("sessionCurrentSequence")];case 4:i=ce.apply(void 0,[l.sent()]),o=i.next(),l.label=5;case 5:if(o.done)return[3,7];s=o.value,n.push(s),l.label=6;case 6:return o=i.next(),[3,5];case 7:return[3,10];case 8:return a=l.sent(),u={error:a},[3,10];case 9:try{o&&!o.done&&(c=i.return)&&c.call(i)}finally{if(u)throw u.error}return[7];case 10:return[2,n]}})})},t})(ym),wR=(function(e){Mt(t,e);function t(){var r=e!==null&&e.apply(this,arguments)||this;return r.finalizedSequences={},r.sequences={},r.sequenceId=0,r}return t.prototype.resetCurrentSequence=function(r){this.sequences[r]=[]},t.prototype.addSequence=function(r){var n=this.sequenceId++,i=Ee([],oe(this.sequences[r]),!1);return this.finalizedSequences[n]={sessionId:r,events:i},this.resetCurrentSequence(r),{sequenceId:n,events:i,sessionId:r}},t.prototype.getSequencesToSend=function(){return x(this,void 0,void 0,function(){return O(this,function(r){return[2,Object.entries(this.finalizedSequences).map(function(n){var i=oe(n,2),o=i[0],s=i[1],a=s.sessionId,u=s.events;return{sequenceId:Number(o),sessionId:a,events:u}})]})})},t.prototype.storeCurrentSequence=function(r){return x(this,void 0,void 0,function(){return O(this,function(n){return this.sequences[r]?[2,this.addSequence(r)]:[2,void 0]})})},t.prototype.addEventToCurrentSequence=function(r,n){return x(this,void 0,void 0,function(){var i;return O(this,function(o){return this.sequences[r]||this.resetCurrentSequence(r),this.shouldSplitEventsList(this.sequences[r],n)&&(i=this.addSequence(r)),this.sequences[r].push(n),[2,i]})})},t.prototype.storeSendingEvents=function(r,n){return x(this,void 0,void 0,function(){return O(this,function(i){return this.finalizedSequences[this.sequenceId]={sessionId:r,events:n},[2,this.sequenceId++]})})},t.prototype.cleanUpSessionEventsStore=function(r,n){return x(this,void 0,void 0,function(){return O(this,function(i){return n!==void 0&&delete this.finalizedSequences[n],[2]})})},t})(ym),$d=function(e){var t=e.config,r=e.minInterval,n=e.maxInterval,i=e.type,o=e.payloadBatcher,s=e.storeType;return x(void 0,void 0,void 0,function(){function a(E){return E===void 0&&(E=!1),x(this,void 0,void 0,function(){return O(this,function(m){return[2,u.flush(E)]})})}var u,c,l,f,d,h,v,p,b;return O(this,function(E){switch(E.label){case 0:return u=new aR(D(D({},t),{loggerProvider:t.loggerProvider,payloadBatcher:o})),c=function(){return new wR({loggerProvider:t.loggerProvider,maxInterval:n,minInterval:r})},l=function(){return x(void 0,void 0,void 0,function(){var m;return O(this,function(g){switch(g.label){case 0:return[4,SR.new(i,{loggerProvider:t.loggerProvider,minInterval:r,maxInterval:n,apiKey:t.apiKey})];case 1:return m=g.sent(),m?[2,m]:(t.loggerProvider.log("Failed to initialize idb store, falling back to memory store."),[2,c()])}})})},s!=="idb"?[3,2]:[4,l()];case 1:return d=E.sent(),[3,3];case 2:d=c(),E.label=3;case 3:return f=d,h=function(m){var g=m.events,y=m.sessionId,_=m.deviceId,S=m.sequenceId;t.debugMode&&sm().then(function(A){var I=A.totalStorageSize,C=A.percentOfQuota,R=A.usageDetails;t.loggerProvider.debug("Total storage size: ".concat(I," KB, percentage of quota: ").concat(C,"%, usage details: ").concat(R))}).catch(function(){}),u.sendEventsList({events:g,sessionId:y,flushMaxRetries:t.flushMaxRetries,apiKey:t.apiKey,deviceId:_,sampleRate:t.sampleRate,serverZone:t.serverZone,version:t.version,type:i,onComplete:function(){return x(void 0,void 0,void 0,function(){return O(this,function(A){switch(A.label){case 0:return[4,f.cleanUpSessionEventsStore(y,S)];case 1:return A.sent(),[2]}})})}})},v=function(m){var g=m.sessionId,y=m.deviceId;f.storeCurrentSequence(g).then(function(_){_&&h({sequenceId:_.sequenceId,events:_.events,sessionId:_.sessionId,deviceId:y})}).catch(function(_){t.loggerProvider.warn("Failed to get current sequence of session replay events for session:",_)})},p=function(m){var g=m.deviceId;return x(void 0,void 0,void 0,function(){var y;return O(this,function(_){switch(_.label){case 0:return[4,f.getSequencesToSend()];case 1:return y=_.sent(),y&&y.forEach(function(S){h({sequenceId:S.sequenceId,events:S.events,sessionId:S.sessionId,deviceId:g})}),[2]}})})},b=function(m){var g=m.event,y=m.sessionId,_=m.deviceId;f.addEventToCurrentSequence(y,g.data).then(function(S){return S&&h({sequenceId:S.sequenceId,events:S.events,sessionId:S.sessionId,deviceId:_})}).catch(function(S){t.loggerProvider.warn("Failed to add event to session replay capture:",S)})},[2,{sendCurrentSequenceEvents:v,addEvent:b,sendStoredEvents:p,flush:a}]}})})},Wd=(function(){function e(){for(var t=[],r=0;r0&&(n=i[0]),Ws(n)}else throw new Error("Selector was not found.")}function AR(e,t){return e.nodeType===Node.DOCUMENT_NODE?e:e===t.root?e.ownerDocument:e}function Co(e,t,r){for(var n=null,i=[],o=e,s=0,a=function(){var c,l,f=new Date().getTime()-bm.getTime();if(Nt.timeoutMs!==void 0&&f>Nt.timeoutMs)throw new Error("Timeout: Can't find a unique selector after ".concat(f,"ms"));var d=Ro(IR(o))||Ro.apply(void 0,Ee([],oe(CR(o)),!1))||Ro.apply(void 0,Ee([],oe(kR(o)),!1))||Ro(RR(o))||[zd()],h=PR(o);if(t=="all")h&&(d=d.concat(d.filter(Ma).map(function(m){return ko(m,h)})));else if(t=="two")d=d.slice(0,1),h&&(d=d.concat(d.filter(Ma).map(function(m){return ko(m,h)})));else if(t=="one"){var v=oe(d=d.slice(0,1),1),p=v[0];h&&Ma(p)&&(d=[ko(p,h)])}else t=="none"&&(d=[zd()],h&&(d=[ko(d[0],h)]));try{for(var b=(c=void 0,ce(d)),E=b.next();!E.done;E=b.next()){var p=E.value;p.level=s}}catch(m){c={error:m}}finally{try{E&&!E.done&&(l=b.return)&&l.call(b)}finally{if(c)throw c.error}}if(i.push(d),i.length>=Nt.seedMinLength&&(n=Gd(i,r),n))return"break";o=o.parentElement,s++};o;){var u=a();if(u==="break")break}return n||(n=Gd(i,r)),!n&&r?r():n}function Gd(e,t){var r,n,i=Sm(_m(e));if(i.length>Nt.threshold)return t?t():null;try{for(var o=ce(i),s=o.next();!s.done;s=o.next()){var a=s.value;if(Em(a))return a}}catch(u){r={error:u}}finally{try{s&&!s.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return null}function Ws(e){for(var t=e[0],r=t.name,n=1;n ").concat(r):r="".concat(e[n].name," ").concat(r),t=e[n]}return r}function Kd(e){return e.map(function(t){return t.penalty}).reduce(function(t,r){return t+r},0)}function Em(e){var t=Ws(e);switch(ql.querySelectorAll(t).length){case 0:throw new Error("Can't select any node with this selector: ".concat(t));case 1:return!0;default:return!1}}function IR(e){var t=e.getAttribute("id");return t&&Nt.idName(t)?{name:"#"+CSS.escape(t),penalty:0}:null}function CR(e){var t=Array.from(e.attributes).filter(function(r){return Nt.attr(r.name,r.value)});return t.map(function(r){return{name:"[".concat(CSS.escape(r.name),'="').concat(CSS.escape(r.value),'"]'),penalty:.5}})}function kR(e){var t=Array.from(e.classList).filter(Nt.className);return t.map(function(r){return{name:"."+CSS.escape(r),penalty:1}})}function RR(e){var t=e.tagName.toLowerCase();return Nt.tagName(t)?{name:t,penalty:2}:null}function zd(){return{name:"*",penalty:3}}function PR(e){var t=e.parentNode;if(!t)return null;var r=t.firstChild;if(!r)return null;for(var n=0;r&&(r.nodeType===Node.ELEMENT_NODE&&n++,r!==e);)r=r.nextSibling;return n}function ko(e,t){return{name:e.name+":nth-child(".concat(t,")"),penalty:e.penalty+1}}function Ma(e){return e.name!=="html"&&!e.name.startsWith("#")}function Ro(){for(var e=[],t=0;t0?r:null}function OR(e){return e!=null}function _m(e,t){var r,n,i,o,s,a;return t===void 0&&(t=[]),O(this,function(u){switch(u.label){case 0:if(!(e.length>0))return[3,9];u.label=1;case 1:u.trys.push([1,6,7,8]),r=ce(e[0]),n=r.next(),u.label=2;case 2:return n.done?[3,5]:(i=n.value,[5,ce(_m(e.slice(1,e.length),t.concat(i)))]);case 3:u.sent(),u.label=4;case 4:return n=r.next(),[3,2];case 5:return[3,8];case 6:return o=u.sent(),s={error:o},[3,8];case 7:try{n&&!n.done&&(a=r.return)&&a.call(r)}finally{if(s)throw s.error}return[7];case 8:return[3,11];case 9:return[4,t];case 10:u.sent(),u.label=11;case 11:return[2]}})}function Sm(e){return Ee([],oe(e),!1).sort(function(t,r){return Kd(t)-Kd(r)})}function wm(e,t,r){var n,i,o;return r===void 0&&(r={counter:0,visited:new Map}),O(this,function(s){switch(s.label){case 0:if(!(e.length>2&&e.length>Nt.optimizedMinLength))return[3,5];n=1,s.label=1;case 1:return nNt.maxNumberOfTries?[2]:(r.counter+=1,i=Ee([],oe(e),!1),i.splice(n,1),o=Ws(i),r.visited.has(o)?[2]:Em(i)&&xR(i,t)?[4,i]:[3,4]):[3,5];case 2:return s.sent(),r.visited.set(o,!0),[5,ce(wm(i,t,r))];case 3:s.sent(),s.label=4;case 4:return n++,[3,1];case 5:return[2]}})}function xR(e,t){return ql.querySelector(Ws(e))===t}var NR=36e5,LR=function(e){var t=e.version,r=e.events,n=[];return r.forEach(function(i){var o=JSON.parse(i);o.count=1,o.type==="click"&&n.push(o)}),{version:t,events:n}},MR=function(e){var t=e.version,r=e.events,n=[];r.forEach(function(o){var s=JSON.parse(o);s.type==="click"&&n.push(s)});var i=n.reduce(function(o,s){var a=s.x,u=s.y,c=s.selector,l=s.timestamp,f=l-l%NR,d="".concat(a,":").concat(u,":").concat(c??"",":").concat(f);return o[d]?o[d].count+=1:o[d]=D(D({},s),{timestamp:f,count:1}),o},{});return{version:t,events:Object.values(i)}},DR=(function(){function e(t,r){var n=this;this.createHook=function(i){var o=i.eventsManager,s=i.sessionId,a=i.deviceIdFn,u=i.mirror,c=i.ugcFilterRules,l=i.performanceOptions;return function(f){if(f.type===cm.Click){var d=le();if(d){var h=d.location,v=d.innerHeight,p=d.innerWidth;if(h){var b=f.x,E=f.y;if(!(b===void 0||E===void 0)){var m=u.getNode(f.id),g;if(m)try{g=TR(m,l)}catch{n.logger.debug("error resolving selector from finder")}var y=Vs(h.href,c),_={x:b+n.scrollWatcher.currentScrollX,y:E+n.scrollWatcher.currentScrollY,selector:g,viewportHeight:v,viewportWidth:p,pageUrl:y,timestamp:Date.now(),type:"click"},S=a();S&&o.addEvent({sessionId:s,event:{type:"interaction",data:JSON.stringify(_)},deviceId:S})}}}}}},this.logger=t,this.scrollWatcher=r}return e})();function Da(){var e=le();return e?.innerHeight||document.documentElement&&document.documentElement.clientHeight||0}function Ua(){var e=le();return e?.innerWidth||document.documentElement&&document.documentElement.clientWidth||0}var UR=(function(){function e(t,r){var n=le();n&&n.navigator&&typeof n.navigator.sendBeacon=="function"?this.sendBeacon=function(i,o){try{if(n.navigator.sendBeacon(i,JSON.stringify(o)))return!0}catch{}return!1}:this.sendBeacon=function(){return!1},this.sendXhr=function(i,o){var s=new XMLHttpRequest;return s.open("POST",i,!0),s.setRequestHeader("Accept","*/*"),s.send(JSON.stringify(o)),!0},this.basePageUrl=Hl(r.serverZone,r.trackServerUrl),this.apiKey=r.apiKey,this.context=t}return e.prototype.send=function(t,r){var n=this.context,i=n.sessionId,o=n.type,s=new URLSearchParams({device_id:t,session_id:String(i),type:String(o),api_key:this.apiKey}),a="".concat(this.basePageUrl,"?").concat(s.toString());this.sendBeacon(a,r)||this.sendXhr(a,r)},e})(),FR=(function(){function e(t,r){var n=this;this.timestamp=Date.now(),this.hook=function(i){n.update(i)},this.send=function(i){return function(o){var s,a,u,c,l=i(),f=le();if(f&&l){var d=(s=f.scrollX)!==null&&s!==void 0?s:0,h=(a=f.scrollY)!==null&&a!==void 0?a:0;(d>0||h>0)&&n.update({id:1,x:d,y:h}),n.transport.send(l,{version:1,events:[{maxScrollX:n._maxScrollX,maxScrollY:n._maxScrollY,maxScrollWidth:n._maxScrollWidth,maxScrollHeight:n._maxScrollHeight,viewportHeight:Da(),viewportWidth:Ua(),pageUrl:Vs(f.location.href,(c=(u=n.config.interactionConfig)===null||u===void 0?void 0:u.ugcFilterRules)!==null&&c!==void 0?c:[]),timestamp:n.timestamp,type:"scroll"}]})}}},this._maxScrollX=0,this._maxScrollY=0,this._currentScrollX=0,this._currentScrollY=0,this._maxScrollWidth=Ua(),this._maxScrollHeight=Da(),this.config=r,this.transport=t}return e.default=function(t,r){return new e(new UR(t,r),r)},Object.defineProperty(e.prototype,"maxScrollX",{get:function(){return this._maxScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollY",{get:function(){return this._maxScrollY},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollWidth",{get:function(){return this._maxScrollWidth},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"maxScrollHeight",{get:function(){return this._maxScrollHeight},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentScrollX",{get:function(){return this._currentScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"currentScrollY",{get:function(){return this._currentScrollY},enumerable:!1,configurable:!0}),e.prototype.update=function(t){var r=Date.now();if(this._currentScrollX=t.x,this._currentScrollY=t.y,t.x>this._maxScrollX){var n=Ua();this._maxScrollX=t.x;var i=t.x+n;i>this._maxScrollWidth&&(this._maxScrollWidth=i),this.timestamp=r}if(t.y>this._maxScrollY){var o=Da();this._maxScrollY=t.y;var s=t.y+o;s>this._maxScrollHeight&&(this._maxScrollHeight=s),this.timestamp=r}},e})(),Xd=(function(){function e(t){var r=t.sessionId,n=t.deviceId;this.deviceId=n,this.sessionId=r,r&&n&&(this.sessionReplayId=xk(r,n))}return e})(),HR=1e3*60*60*24*2,BR=(function(){function e(){var t=this;this.dbs={},this.createStore=function(r){return x(t,void 0,void 0,function(){return O(this,function(n){switch(n.label){case 0:return[4,gm(r,1,{upgrade:function(i){i.objectStoreNames.contains("sessionTargetingMatch")||i.createObjectStore("sessionTargetingMatch",{keyPath:"sessionId"})}})];case 1:return[2,n.sent()]}})})},this.openOrCreateDB=function(r){return x(t,void 0,void 0,function(){var n,i;return O(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(n="".concat(r.substring(0,10),"_amp_session_replay_targeting"),[4,this.createStore(n)]);case 1:return i=o.sent(),this.dbs[r]=i,[2,i]}})})},this.getTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId;return x(t,void 0,void 0,function(){var s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return s=l.sent(),a=String(o),[4,s.get("sessionTargetingMatch",a)];case 2:return u=l.sent(),[2,u?.targetingMatch];case 3:return c=l.sent(),ur(n,"Failed to get targeting match for session id ".concat(o,": ").concat(c),c),[3,4];case 4:return[2,void 0]}})})},this.storeTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId,s=r.targetingMatch;return x(t,void 0,void 0,function(){var a,u,c,l;return O(this,function(f){switch(f.label){case 0:return f.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return a=f.sent(),u=String(o),[4,a.put("sessionTargetingMatch",{targetingMatch:s,sessionId:u,lastUpdated:Date.now()})];case 2:return c=f.sent(),[2,c];case 3:return l=f.sent(),ur(n,"Failed to store targeting match for session id ".concat(o,": ").concat(l),l),[3,4];case 4:return[2,void 0]}})})},this.clearStoreOfOldSessions=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.currentSessionId;return x(t,void 0,void 0,function(){var s,a,u,c,l,f,d,h;return O(this,function(v){switch(v.label){case 0:return v.trys.push([0,8,,9]),[4,this.openOrCreateDB(i)];case 1:return s=v.sent(),a=String(o),u=s.transaction("sessionTargetingMatch","readwrite"),[4,u.store.getAll()];case 2:c=v.sent(),l=0,v.label=3;case 3:return lHR?[4,u.store.delete(f.sessionId)]:[3,5]):[3,6];case 4:v.sent(),v.label=5;case 5:return l++,[3,3];case 6:return[4,u.done];case 7:return v.sent(),[3,9];case 8:return h=v.sent(),ur(n,"Failed to clear old targeting matches for sessions: ".concat(h),h),[3,9];case 9:return[2]}})})}}return e})(),Fa=new BR,jR=function(e){var t=e.sessionId,r=e.targetingConfig,n=e.loggerProvider,i=e.apiKey,o=e.targetingParams,s=e.urlChange,a=s===void 0?!1:s;return x(void 0,void 0,void 0,function(){var u,c,l,f,d,h,v;return O(this,function(p){switch(p.label){case 0:return[4,Fa.clearStoreOfOldSessions({loggerProvider:n,apiKey:i,currentSessionId:t})];case 1:return p.sent(),[4,Fa.getTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:t})];case 2:if(u=p.sent(),u===!0&&!a)return[2,!0];c=!0,p.label=3;case 3:return p.trys.push([3,6,,7]),[4,Bt(()=>import("./PJVKHxNq.js"),[],import.meta.url)];case 4:return l=p.sent().evaluateTargeting,f=D(D({},o),{flag:r,sessionId:typeof t=="string"?parseInt(t,10):t,apiKey:i,loggerProvider:n}),[4,l(f)];case 5:return d=p.sent(),d&&d.sr_targeting_config&&(c=d.sr_targeting_config.key==="on"),Fa.storeTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:t,targetingMatch:c}),[3,7];case 6:return h=p.sent(),v=h,n.warn(v.message),[3,7];case 7:return[2,c]}})})},Vo=2654435761,$o=2246822519,Yd=3266489917,qR=668265263,Jd=374761393;function zr(e,t){return(e<>>32-t)>>>0}function Po(e,t){return e=e+Math.imul(t,$o)>>>0,e=zr(e,13),e=Math.imul(e,Vo)>>>0,e}function gi(e,t){return(e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24)>>>0}function VR(e){for(var t=[],r=0;r=55296&&n<=56319&&r+1=56320&&i<=57343&&(n=(n-55296<<10)+(i-56320)+65536,r++)}n<128?t.push(n):n<2048?t.push(192|n>>6,128|n&63):n<65536?t.push(224|n>>12,128|n>>6&63,128|n&63):t.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|n&63)}return new Uint8Array(t)}function $R(e,t){t===void 0&&(t=0);var r=VR(e),n=r.length,i,o=0;if(n>=16){for(var s=t+Vo+$o>>>0,a=t+$o>>>0,u=t>>>0,c=t-Vo>>>0;o<=n-16;)s=Po(s,gi(r,o)),o+=4,a=Po(a,gi(r,o)),o+=4,u=Po(u,gi(r,o)),o+=4,c=Po(c,gi(r,o)),o+=4;i=zr(s,1)+zr(a,7)+zr(u,12)+zr(c,18)>>>0}else i=t+Jd>>>0;for(i=i+n>>>0;o<=n-4;)i=i+Math.imul(gi(r,o),Yd)>>>0,i=Math.imul(zr(i,17),qR)>>>0,o+=4;for(;o>>0,i=Math.imul(zr(i,11),Vo)>>>0,o++;return i^=i>>>15,i=Math.imul(i,$o)>>>0,i^=i>>>13,i=Math.imul(i,Yd)>>>0,i^=i>>>16,i>>>0}function WR(e,t){var r=$R(e.toString()),n=r%1e6;return n/1e60?Vs(y,u):y;return{href:S,title:_,viewportHeight:E,viewportWidth:m,type:"url-change-event"}},p=function(){var E=h();if(d===void 0||E!==d){d=E;var m=v();t(m)}},b=Tm(r,function(){return p()},c?{enablePolling:!0,pollingInterval:l}:{});return p(),function(){return b()}},options:e}}Am();var GR=(function(){function e(){var t=this;this.name="@amplitude/session-replay-browser",this.recordCancelCallback=null,this.eventCount=0,this.sessionTargetingMatch=!1,this.pageLeaveFns=[],this.recordFunction=null,this.urlChangeCleanup=null,this.latestUrlChangeTargetingEvaluationId=0,this.teardownEventListeners=function(r){var n=le();n&&(n.removeEventListener("blur",t.blurListener),n.removeEventListener("focus",t.focusListener),!r&&n.addEventListener("blur",t.blurListener),!r&&n.addEventListener("focus",t.focusListener),n.self&&"onpagehide"in n.self?(n.removeEventListener("pagehide",t.pageLeaveListener),!r&&n.addEventListener("pagehide",t.pageLeaveListener)):(n.removeEventListener("beforeunload",t.pageLeaveListener),!r&&n.addEventListener("beforeunload",t.pageLeaveListener)))},this.blurListener=function(){t.sendEvents()},this.focusListener=function(){t.recordEvents(!1)},this.pageLeaveListener=function(r){t.pageLeaveFns.forEach(function(n){n(r)})},this.evaluateTargetingAndCapture=function(r,n,i,o){return n===void 0&&(n=!1),i===void 0&&(i=!1),o===void 0&&(o=!1),x(t,void 0,void 0,function(){var s,a,u,c,l,f,d,h,v,p,b,E,m;return O(this,function(g){switch(g.label){case 0:if(!this.identifiers||!this.identifiers.sessionId||!this.config)return this.identifiers&&!this.identifiers.sessionId?this.loggerProvider.log("Session ID has not been set yet, cannot evaluate targeting for Session Replay."):this.loggerProvider.warn("Session replay init has not been called, cannot evaluate targeting."),[2];if(!this.config.targetingConfig)if(n)this.loggerProvider.log("Targeting config has not been set yet, cannot evaluate targeting.");else return this.loggerProvider.log("No targeting config set, skipping initialization/recording for event."),[2];return this.lastTargetingParams=r,s=this.config.targetingConfig,a=s&&!this.sessionTargetingMatch,a?(u=o?this.latestUrlChangeTargetingEvaluationId:void 0,c=r.event,c&&Object.values(_t).includes(c.event_type)&&(c=void 0),l=(E=(v=(h=r.page)===null||h===void 0?void 0:h.url)!==null&&v!==void 0?v:(b=(p=le())===null||p===void 0?void 0:p.location)===null||b===void 0?void 0:b.href)!==null&&E!==void 0?E:"",f=(m=r.page)!==null&&m!==void 0?m:l!==""?{url:l}:void 0,[4,jR({sessionId:this.identifiers.sessionId,targetingConfig:s,loggerProvider:this.loggerProvider,apiKey:this.config.apiKey,targetingParams:{userProperties:r.userProperties,event:c,page:f},urlChange:o})]):[3,2];case 1:if(d=g.sent(),o&&u!==void 0&&u!==this.latestUrlChangeTargetingEvaluationId)return this.loggerProvider.debug("Ignoring stale URL-change targeting result #".concat(u,"; latest is #").concat(this.latestUrlChangeTargetingEvaluationId,".")),[2];this.sessionTargetingMatch=this.sessionTargetingMatch||d,this.loggerProvider.debug(JSON.stringify({name:"targeted replay capture config",sessionTargetingMatch:this.sessionTargetingMatch,event:c,targetingParams:r},null,2)),g.label=2;case 2:return n?(this.initialize(!0),[3,5]):[3,3];case 3:return i||!this.recordCancelCallback?(this.loggerProvider.log("Recording events for session due to forceRestart or no ongoing recording."),[4,this.recordEvents()]):[3,5];case 4:g.sent(),g.label=5;case 5:return[2]}})})},this.addCustomRRWebEvent=function(r,n,i){return n===void 0&&(n={}),i===void 0&&(i=!0),x(t,void 0,void 0,function(){var o,s,a,u;return O(this,function(c){switch(c.label){case 0:return c.trys.push([0,3,,4]),o=void 0,s=this.config,s&&r!==kr.METADATA?(o={config:am(s),version:Fu},i?[4,sm()]:[3,2]):[3,2];case 1:a=c.sent(),o=D(D({},a),o),c.label=2;case 2:return this.recordCancelCallback&&this.recordFunction?this.recordFunction.addCustomEvent(r,D(D({},n),o)):this.loggerProvider.debug("Not able to add custom replay capture event ".concat(r," due to no ongoing recording.")),[3,4];case 3:return u=c.sent(),this.loggerProvider.debug("Error while adding custom replay capture event: ",u),[3,4];case 4:return[2]}})})},this.stopRecordingEvents=function(){var r;try{t.loggerProvider.log("Session Replay capture stopping."),t.recordCancelCallback&&t.recordCancelCallback(),t.recordCancelCallback=null,(r=t.networkObservers)===null||r===void 0||r.stop()}catch(i){var n=i;t.loggerProvider.warn("Error occurred while stopping replay capture: ".concat(n.toString()))}},this.loggerProvider=new xu(new Kn)}return e.prototype.init=function(t,r){return tt(this._init(t,r))},e.prototype.setupUrlChangeListenerForTargeting=function(){var t=this,r;(r=this.urlChangeCleanup)===null||r===void 0||r.call(this);var n=le();if(n?.location){var i=function(s){var a=++t.latestUrlChangeTargetingEvaluationId;t.evaluateTargetingAndCapture({userProperties:{},event:void 0,page:{url:s}},!1,!1,!0),t.loggerProvider.debug("Queued URL-change targeting re-evaluation #".concat(a," for ").concat(s,"."))},o=Tm(n,i);this.urlChangeCleanup=function(){o(),t.urlChangeCleanup=null}}},e.prototype.getCurrentPageForTargeting=function(){var t,r,n=(r=(t=le())===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.href;return n!=null?{url:n}:void 0},e.prototype._init=function(t,r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h,v,p,b,E,m,g,y,I,_,S,A,I,C,R,P,k;return O(this,function(N){switch(N.label){case 0:return(n=this.urlChangeCleanup)===null||n===void 0||n.call(this),this.loggerProvider=new xu(r.loggerProvider||new Kn),Object.prototype.hasOwnProperty.call(r,"logLevel")&&this.loggerProvider.enable(r.logLevel),this.identifiers=new Xd({sessionId:r.sessionId,deviceId:r.deviceId}),f=this,[4,Hk(t,r)];case 1:return f.joinedConfigGenerator=N.sent(),[4,this.joinedConfigGenerator.generateJoinedConfig()];case 2:d=N.sent(),h=d.joinedConfig,v=d.localConfig,p=d.remoteConfig,this.config=h,this.setMetadata(r.sessionId,h,v,p,(i=r.version)===null||i===void 0?void 0:i.version,Fu,(o=r.version)===null||o===void 0?void 0:o.type),r.sessionId&&(!((s=this.config.interactionConfig)===null||s===void 0)&&s.enabled)&&(b=FR.default({sessionId:r.sessionId,type:"interaction"},this.config),this.pageLeaveFns=[b.send(this.getDeviceId.bind(this)).bind(b)],this.scrollHook=b.hook.bind(b),this.clickHandler=new DR(this.loggerProvider,b)),E=[],m=this.config.storeType,m==="idb"&&!(!((a=le())===null||a===void 0)&&a.indexedDB)&&(m="memory",this.loggerProvider.warn("Could not use preferred indexedDB storage, reverting to in memory option.")),this.loggerProvider.log("Using ".concat(m," for event storage.")),N.label=3;case 3:return N.trys.push([3,5,,6]),[4,$d({config:this.config,type:"replay",storeType:m})];case 4:return g=N.sent(),E.push({name:"replay",manager:g}),[3,6];case 5:return y=N.sent(),I=y,this.loggerProvider.warn("Error occurred while creating replay events manager: ".concat(I.toString())),[3,6];case 6:if(!(!((u=this.config.interactionConfig)===null||u===void 0)&&u.enabled))return[3,10];_=this.config.interactionConfig.batch?MR:LR,N.label=7;case 7:return N.trys.push([7,9,,10]),[4,$d({config:this.config,type:"interaction",minInterval:(c=this.config.interactionConfig.trackEveryNms)!==null&&c!==void 0?c:wk,maxInterval:Tk,payloadBatcher:_,storeType:m})];case 8:return S=N.sent(),E.push({name:"interaction",manager:S}),[3,10];case 9:return A=N.sent(),I=A,this.loggerProvider.warn("Error occurred while creating interaction events manager: ".concat(I.toString())),[3,10];case 10:return this.eventsManager=new(Wd.bind.apply(Wd,Ee([void 0],oe(E),!1))),this.eventCompressor&&this.eventCompressor.terminate(),C=void 0,R=le(),this.config.useWebWorker&&R&&R.Worker?[4,Bt(()=>import("./D7Bf6Say.js"),[],import.meta.url)]:[3,12];case 11:P=N.sent().compressionScript,C=P,N.label=12;case 12:return this.eventCompressor=new rR(this.eventsManager,this.config,this.getDeviceId(),C),[4,this.initializeNetworkObservers()];case 13:return N.sent(),!((l=le())===null||l===void 0)&&l.opener&&(k=fg(),dg(k),k.setup(D({logger:this.loggerProvider},this.config.serverZone&&{endpoint:lg[this.config.serverZone]}))),this.loggerProvider.log("Installing @amplitude/session-replay-browser."),this.teardownEventListeners(!1),[4,this.evaluateTargetingAndCapture({userProperties:r.userProperties,page:this.getCurrentPageForTargeting()},!0)];case 14:return N.sent(),this.config.targetingConfig&&this.setupUrlChangeListenerForTargeting(),[2]}})})},e.prototype.setSessionId=function(t,r){return tt(this.asyncSetSessionId(t,r))},e.prototype.asyncSetSessionId=function(t,r,n){var i;return x(this,void 0,void 0,function(){var o,s,a;return O(this,function(u){switch(u.label){case 0:return this.latestUrlChangeTargetingEvaluationId++,this.sessionTargetingMatch=!1,this.lastShouldRecordDecision=void 0,o=this.identifiers&&this.identifiers.sessionId,o&&this.sendEvents(o),s=r||this.getDeviceId(),this.identifiers=new Xd({sessionId:t,deviceId:s}),this.joinedConfigGenerator&&o?[4,this.joinedConfigGenerator.generateJoinedConfig()]:[3,2];case 1:a=u.sent().joinedConfig,this.config=a,u.label=2;case 2:return!((i=this.config)===null||i===void 0)&&i.targetingConfig?[4,this.evaluateTargetingAndCapture({userProperties:n?.userProperties,page:this.getCurrentPageForTargeting()},!1,!0)]:[3,4];case 3:return u.sent(),[3,6];case 4:return[4,this.recordEvents()];case 5:u.sent(),u.label=6;case 6:return[2]}})})},e.prototype.getSessionReplayProperties=function(){var t,r=this.config,n=this.identifiers;if(!r||!n)return this.loggerProvider.warn("Session replay init has not been called, cannot get session replay properties."),{};var i=this.getShouldRecord(),o={};return i&&(o=(t={},t[pk]=n.sessionReplayId?n.sessionReplayId:null,t),r.debugMode&&(o[bk]=JSON.stringify({appHash:Su(r.apiKey).toString()}))),this.addCustomRRWebEvent(kr.GET_SR_PROPS,{shouldRecord:i,eventProperties:o},this.eventCount===10),this.eventCount===10&&(this.eventCount=0),this.eventCount++,o},e.prototype.sendEvents=function(t){var r,n=t||((r=this.identifiers)===null||r===void 0?void 0:r.sessionId),i=this.getDeviceId();this.eventsManager&&n&&i&&this.eventsManager.sendCurrentSequenceEvents({sessionId:n,deviceId:i})},e.prototype.initialize=function(t){var r;return t===void 0&&(t=!1),x(this,void 0,void 0,function(){var n;return O(this,function(i){return!((r=this.identifiers)===null||r===void 0)&&r.sessionId?(n=this.getDeviceId(),n?(this.eventsManager&&t&&this.eventsManager.sendStoredEvents({deviceId:n}),[2,this.recordEvents()]):(this.loggerProvider.log("Session is not being recorded due to lack of device id."),[2,Promise.resolve()])):(this.loggerProvider.log("Session is not being recorded due to lack of session id."),[2,Promise.resolve()])})})},e.prototype.shouldOptOut=function(){var t,r,n;if(!((t=this.config)===null||t===void 0)&&t.instanceName){var i=zn(this.config.instanceName).identityStore;n=i.getIdentity().optOut}return n!==void 0?n:(r=this.config)===null||r===void 0?void 0:r.optOut},e.prototype.getShouldRecord=function(){if(!this.identifiers||!this.config||!this.identifiers.sessionId)return this.loggerProvider.warn("Session is not being recorded due to lack of config, please call sessionReplay.init."),!1;if(!this.config.captureEnabled)return this.loggerProvider.log("Session ".concat(this.identifiers.sessionId," not being captured due to capture being disabled for project or because the remote config could not be fetched.")),!1;if(this.shouldOptOut())return this.loggerProvider.log("Opting session ".concat(this.identifiers.sessionId," out of recording due to optOut config.")),!1;var t=!1,r="",n=!1;if(this.config.targetingConfig)this.sessionTargetingMatch?(r="Capturing replays for session ".concat(this.identifiers.sessionId," due to matching targeting conditions."),this.loggerProvider.log(r),t=!0,n=!0):(r="Not capturing replays for session ".concat(this.identifiers.sessionId," due to not matching targeting conditions."),this.loggerProvider.log(r),t=!1,n=!1);else{var i=WR(this.identifiers.sessionId,this.config.sampleRate);i?(t=!0,n=!0):(r="Opting session ".concat(this.identifiers.sessionId," out of recording due to sample rate."),this.loggerProvider.log(r),t=!1,n=!1)}return this.lastShouldRecordDecision!==t&&this.config.targetingConfig&&(this.addCustomRRWebEvent(kr.TARGETING_DECISION,{message:r,sessionId:this.identifiers.sessionId,matched:n,targetingParams:this.lastTargetingParams}),this.lastShouldRecordDecision=t),t},e.prototype.getBlockSelectors=function(){var t,r,n,i=(n=(r=(t=this.config)===null||t===void 0?void 0:t.privacyConfig)===null||r===void 0?void 0:r.blockSelector)!==null&&n!==void 0?n:[];if(i.length!==0)return i},e.prototype.getMaskTextSelectors=function(){var t,r,n,i;if(((r=(t=this.config)===null||t===void 0?void 0:t.privacyConfig)===null||r===void 0?void 0:r.defaultMaskLevel)==="conservative")return"*";var o=(i=(n=this.config)===null||n===void 0?void 0:n.privacyConfig)===null||i===void 0?void 0:i.maskSelector;if(o)return o},e.prototype.getRecordingPlugins=function(t){var r,n,i,o,s,a;return x(this,void 0,void 0,function(){var u,c,l,f;return O(this,function(d){switch(d.label){case 0:u=[];try{c=Am({ugcFilterRules:((n=(r=this.config)===null||r===void 0?void 0:r.interactionConfig)===null||n===void 0?void 0:n.ugcFilterRules)||[],enablePolling:((i=this.config)===null||i===void 0?void 0:i.enableUrlChangePolling)||!1,pollingInterval:(o=this.config)===null||o===void 0?void 0:o.urlChangePollingInterval,captureDocumentTitle:(s=this.config)===null||s===void 0?void 0:s.captureDocumentTitle}),u.push(c)}catch(h){this.loggerProvider.warn("Failed to create URL tracking plugin:",h)}if(!(!((a=t?.console)===null||a===void 0)&&a.enabled))return[3,4];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,Bt(()=>import("./sPdhR0i8.js"),[],import.meta.url)];case 2:return l=d.sent().getRecordConsolePlugin,u.push(l({level:t.console.levels})),[3,4];case 3:return f=d.sent(),this.loggerProvider.warn("Failed to load console plugin:",f),[3,4];case 4:return[2,u.length>0?u:void 0]}})})},e.prototype.getRecordFunction=function(){return x(this,void 0,void 0,function(){var t,r;return O(this,function(n){switch(n.label){case 0:if(this.recordFunction)return[2,this.recordFunction];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,Bt(()=>import("./DMrp4Fbu.js"),[],import.meta.url)];case 2:return t=n.sent().record,this.recordFunction=t,[2,t];case 3:return r=n.sent(),this.loggerProvider.warn("Failed to load rrweb-record module:",r),[2,null];case 4:return[2]}})})},e.prototype.recordEvents=function(t){var r,n,i,o,s,a,u,c;return t===void 0&&(t=!0),x(this,void 0,void 0,function(){var l,f,d,h,v,p,b,E,m,g,y,_,S,A,I,C,R=this;return O(this,function(P){switch(P.label){case 0:return l=this.config,f=this.getShouldRecord(),d=(r=this.identifiers)===null||r===void 0?void 0:r.sessionId,!f||!d||!l?[2]:(this.stopRecordingEvents(),[4,this.getRecordFunction()]);case 1:return h=P.sent(),h?[4,this.initializeNetworkObservers()]:[2];case 2:P.sent(),v=(n=l.loggingConfig)===null||n===void 0?void 0:n.network,p=Hl(l.serverZone,l.trackServerUrl),b=[em,tm,rm,p],(i=this.networkObservers)===null||i===void 0||i.start(function(k){b.some(function(N){return k.url.startsWith(N)})||R.addCustomRRWebEvent(kr.FETCH_REQUEST,k)},v),E=l.privacyConfig,m=l.interactionConfig,g=l.loggingConfig,y=m?.enabled?{mouseInteraction:this.eventsManager&&((o=this.clickHandler)===null||o===void 0?void 0:o.createHook({eventsManager:this.eventsManager,sessionId:d,deviceIdFn:this.getDeviceId.bind(this),mirror:h.mirror,ugcFilterRules:(s=m.ugcFilterRules)!==null&&s!==void 0?s:[],performanceOptions:(a=l.performanceConfig)===null||a===void 0?void 0:a.interaction})),scroll:this.scrollHook}:{},_=m?.enabled&&m.ugcFilterRules?m.ugcFilterRules:[],this.loggerProvider.log("Session Replay capture beginning for ".concat(d,".")),P.label=3;case 3:return P.trys.push([3,5,,6]),S=this,A=h,C={emit:function(k){if(R.shouldOptOut()){R.loggerProvider.log("Opting session ".concat(d," out of recording due to optOut config.")),R.stopRecordingEvents(),R.sendEvents();return}k.type===lm.Meta&&(k.data.href=Vs(k.data.href,_)),R.eventCompressor&&R.eventCompressor.enqueueEvent(k,d)},inlineStylesheet:l.shouldInlineStylesheet,hooks:y,maskAllInputs:!0,maskTextClass:Zg,blockClass:Ek,blockSelector:this.getBlockSelectors(),applyBackgroundColorToBlockedElements:l.applyBackgroundColorToBlockedElements,maskInputFn:Nd("input",E),maskTextFn:Nd("text",E),maskAttributeFn:Pk(E),maskTextSelector:this.getMaskTextSelectors(),recordCanvas:!1,slimDOMOptions:{script:(u=l.omitElementTags)===null||u===void 0?void 0:u.script,comment:(c=l.omitElementTags)===null||c===void 0?void 0:c.comment},errorHandler:function(k){var N=k;if(N.message.includes("insertRule")&&N.message.includes("CSSStyleSheet")||N._external_)throw N;return R.loggerProvider.warn("Error while capturing replay: ",N.toString()),!0}},[4,this.getRecordingPlugins(g)];case 4:return S.recordCancelCallback=A.apply(void 0,[(C.plugins=P.sent(),C)]),this.addCustomRRWebEvent(kr.DEBUG_INFO),t&&this.addCustomRRWebEvent(kr.METADATA,this.metadata),[3,6];case 5:return I=P.sent(),this.loggerProvider.warn("Failed to initialize session replay:",I),[3,6];case 6:return[2]}})})},e.prototype.getDeviceId=function(){var t;return(t=this.identifiers)===null||t===void 0?void 0:t.deviceId},e.prototype.getSessionId=function(){var t;return(t=this.identifiers)===null||t===void 0?void 0:t.sessionId},e.prototype.flush=function(t){var r;return t===void 0&&(t=!1),x(this,void 0,void 0,function(){return O(this,function(n){return[2,(r=this.eventsManager)===null||r===void 0?void 0:r.flush(t)]})})},e.prototype.shutdown=function(){var t;(t=this.urlChangeCleanup)===null||t===void 0||t.call(this),this.teardownEventListeners(!0),this.stopRecordingEvents(),this.sendEvents()},e.prototype.mapSDKType=function(t){return t==="plugin"?"@amplitude/plugin-session-replay-browser":t==="segment"?"@amplitude/segment-session-replay-plugin":null},e.prototype.setMetadata=function(t,r,n,i,o,s,a){var u=t?.toString()?Su(t.toString()):void 0;this.metadata={joinedConfig:r,localConfig:n,remoteConfig:i,sessionId:t,hashValue:u,sampleRate:r.sampleRate,replaySDKType:this.mapSDKType(a),replaySDKVersion:o,standaloneSDKType:"@amplitude/session-replay-browser",standaloneSDKVersion:s}},e.prototype.initializeNetworkObservers=function(){var t,r,n;return x(this,void 0,void 0,function(){var i,o;return O(this,function(s){switch(s.label){case 0:if(!(!((n=(r=(t=this.config)===null||t===void 0?void 0:t.loggingConfig)===null||r===void 0?void 0:r.network)===null||n===void 0)&&n.enabled&&!this.networkObservers))return[3,4];s.label=1;case 1:return s.trys.push([1,3,,4]),[4,Bt(()=>import("./CrxCT0gX.js"),[],import.meta.url)];case 2:return i=s.sent().NetworkObservers,this.networkObservers=new i,[3,4];case 3:return o=s.sent(),this.loggerProvider.warn("Failed to import or instantiate NetworkObservers:",o),[3,4];case 4:return[2]}})})},e})(),Wr=function(e){return function(){var t=e.config,r=t||um(),n=r.loggerProvider,i=r.logLevel;return{logger:n,logLevel:i}}},KR=function(){var e=new GR;return{init:_e(e.init.bind(e),"init",Wr(e)),evaluateTargetingAndCapture:_e(e.evaluateTargetingAndCapture.bind(e),"evaluateTargetingAndRecord",Wr(e)),setSessionId:_e(e.setSessionId.bind(e),"setSessionId",Wr(e)),getSessionId:_e(e.getSessionId.bind(e),"getSessionId",Wr(e)),getSessionReplayProperties:_e(e.getSessionReplayProperties.bind(e),"getSessionReplayProperties",Wr(e)),flush:_e(e.flush.bind(e),"flush",Wr(e)),shutdown:_e(e.shutdown.bind(e),"shutdown",Wr(e))}};const dn=KR();var zR=dn.init,XR=dn.setSessionId,YR=dn.getSessionId,JR=dn.getSessionReplayProperties,QR=dn.flush,ZR=dn.shutdown,eP=dn.evaluateTargetingAndCapture,tP=function(e){return e===void 0&&(e="$default_instance"),Al.getInstance(e)},Rr;(function(e){e.SET="$set",e.SET_ONCE="$setOnce",e.ADD="$add",e.APPEND="$append",e.PREPEND="$prepend",e.REMOVE="$remove",e.PREINSERT="$preInsert",e.POSTINSERT="$postInsert",e.UNSET="$unset",e.CLEAR_ALL="$clearAll"})(Rr||(Rr={}));var Zd;(function(e){e.REVENUE_PRODUCT_ID="$productId",e.REVENUE_QUANTITY="$quantity",e.REVENUE_PRICE="$price",e.REVENUE_TYPE="$revenueType",e.REVENUE_CURRENCY="$currency",e.REVENUE="$revenue"})(Zd||(Zd={}));var eh;(function(e){e.IDENTIFY="$identify",e.GROUP_IDENTIFY="$groupidentify",e.REVENUE="revenue_amount"})(eh||(eh={}));var rP=[Rr.SET,Rr.SET_ONCE,Rr.ADD,Rr.APPEND,Rr.PREPEND,Rr.POSTINSERT],nP=function(e){if(e.user_properties){var t={},r=Object.keys(e.user_properties);return r.forEach(function(n){if(rP.includes(n)){var i=e.user_properties&&e.user_properties[n];t=D(D({},t),i)}}),t}},th="1.27.6",iP="[Amplitude]",oP="".concat(iP," Session Replay ID"),$u=(function(){function e(t){this.name=e.pluginName,this.type="enrichment",this.config=null,this.sessionReplay={flush:QR,getSessionId:YR,getSessionReplayProperties:JR,init:zR,setSessionId:XR,shutdown:ZR,evaluateTargetingAndCapture:eP},this.options=D({forceSessionTracking:!1},t),this.srInitOptions=this.options}return e.prototype.setup=function(t,r){var n,i,o,s,a,u,c,l;return x(this,void 0,void 0,function(){var f,d,h;return O(this,function(v){switch(v.label){case 0:return v.trys.push([0,2,,3]),t?.loggerProvider.log("Installing @amplitude/plugin-session-replay, version ".concat(th,".")),this.config=t,this.options.forceSessionTracking&&(typeof t.defaultTracking=="boolean"?t.defaultTracking===!1&&(t.defaultTracking={pageViews:!1,formInteractions:!1,fileDownloads:!1,sessions:!0}):t.defaultTracking=D(D({},t.defaultTracking),{sessions:!0})),f=tP(this.config.instanceName).identityStore,d=f.getIdentity().userProperties,this.srInitOptions={instanceName:this.config.instanceName,deviceId:(n=this.options.deviceId)!==null&&n!==void 0?n:this.config.deviceId,optOut:this.config.optOut,sessionId:this.options.customSessionId?void 0:this.config.sessionId,loggerProvider:this.config.loggerProvider,logLevel:this.config.logLevel,flushMaxRetries:this.config.flushMaxRetries,serverZone:this.config.serverZone,configServerUrl:this.options.configServerUrl||((i=this.config.remoteConfig)===null||i===void 0?void 0:i.serverUrl),trackServerUrl:this.options.trackServerUrl,sampleRate:this.options.sampleRate,privacyConfig:{blockSelector:(o=this.options.privacyConfig)===null||o===void 0?void 0:o.blockSelector,maskSelector:(s=this.options.privacyConfig)===null||s===void 0?void 0:s.maskSelector,unmaskSelector:(a=this.options.privacyConfig)===null||a===void 0?void 0:a.unmaskSelector,defaultMaskLevel:(u=this.options.privacyConfig)===null||u===void 0?void 0:u.defaultMaskLevel},debugMode:this.options.debugMode,shouldInlineStylesheet:this.options.shouldInlineStylesheet,version:{type:"plugin",version:th},performanceConfig:this.options.performanceConfig,storeType:this.options.storeType,useWebWorker:(c=this.options.useWebWorker)!==null&&c!==void 0?c:(l=this.options.experimental)===null||l===void 0?void 0:l.useWebWorker,userProperties:d,omitElementTags:this.options.omitElementTags,applyBackgroundColorToBlockedElements:this.options.applyBackgroundColorToBlockedElements,interactionConfig:this.options.interactionConfig,captureDocumentTitle:this.options.captureDocumentTitle,enableUrlChangePolling:this.options.enableUrlChangePolling,urlChangePollingInterval:this.options.urlChangePollingInterval},[4,this.sessionReplay.init(t.apiKey,this.srInitOptions).promise];case 1:return v.sent(),[3,3];case 2:return h=v.sent(),t?.loggerProvider.error("Session Replay: Failed to initialize due to ".concat(h.message)),[3,3];case 3:return[2]}})})},e.prototype.onSessionIdChanged=function(t){var r;return x(this,void 0,void 0,function(){return O(this,function(n){switch(n.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("Analytics session id is changed to ".concat(t,", SR session id is ").concat(String(this.sessionReplay.getSessionId()),".")),[4,this.sessionReplay.setSessionId(t).promise];case 1:return n.sent(),[2]}})})},e.prototype.onOptOutChanged=function(t){var r;return x(this,void 0,void 0,function(){var n;return O(this,function(i){switch(i.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("optOut is changed to ".concat(String(t),", calling ").concat(t?"sessionReplay.shutdown()":"sessionReplay.init()",".")),t?(this.sessionReplay.shutdown(),[3,4]):[3,1];case 1:return n=this.config!=null,n?[4,this.sessionReplay.init(this.config.apiKey,this.srInitOptions).promise]:[3,3];case 2:n=i.sent(),i.label=3;case 3:i.label=4;case 4:return[2]}})})},e.prototype.execute=function(t){var r,n,i;return x(this,void 0,void 0,function(){var o,u,o,s,a,u,c;return O(this,function(l){switch(l.label){case 0:return l.trys.push([0,9,,10]),this.options.customSessionId?(o=this.options.customSessionId(t),o?o===this.sessionReplay.getSessionId()?[3,2]:[4,this.sessionReplay.setSessionId(o).promise]:[3,3]):[3,4];case 1:l.sent(),l.label=2;case 2:u=this.sessionReplay.getSessionReplayProperties(),t.event_properties=D(D({},t.event_properties),u),l.label=3;case 3:return[3,8];case 4:return o=(r=this.config)===null||r===void 0?void 0:r.sessionId,o&&o!==this.sessionReplay.getSessionId()?[4,this.sessionReplay.setSessionId(o).promise]:[3,6];case 5:l.sent(),l.label=6;case 6:return o&&o===t.session_id?(s=void 0,t.event_type===_t.IDENTIFY&&(s=nP(t)),a=le(),[4,this.sessionReplay.evaluateTargetingAndCapture({event:t,userProperties:s,page:((n=a?.location)===null||n===void 0?void 0:n.href)!=null?{url:a.location.href}:void 0})]):[3,8];case 7:l.sent(),u=this.sessionReplay.getSessionReplayProperties(),t.event_properties=D(D({},t.event_properties),u),l.label=8;case 8:return t.event_type===_t.IDENTIFY&&t.event_properties&&delete t.event_properties[oP],[2,Promise.resolve(t)];case 9:return c=l.sent(),(i=this.config)===null||i===void 0||i.loggerProvider.error("Session Replay: Failed to enrich event due to ".concat(c.message)),[2,Promise.resolve(t)];case 10:return[2]}})})},e.prototype.teardown=function(){var t;return x(this,void 0,void 0,function(){return O(this,function(r){try{this.sessionReplay.shutdown(),this.config=null}catch(n){(t=this.config)===null||t===void 0||t.loggerProvider.error("Session Replay: teardown failed due to ".concat(n.message))}return[2]})})},e.prototype.getSessionReplayProperties=function(){return this.sessionReplay.getSessionReplayProperties()},e.pluginName="@amplitude/plugin-session-replay-browser",e})(),sP=function(e){return new $u(e)},Wu=function(e,t){return Wu=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Wu(e,t)};function Im(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");Wu(e,t);function r(){this.constructor=e}e.prototype=t===null?Object.create(t):(r.prototype=t.prototype,new r)}var Or=function(){return Or=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Vl(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function $l(e,t,r){if(arguments.length===2)for(var n=0,i=t.length,o;n>6|192,t[r++]=i&63|128):(i&64512)==55296&&n+1>18|240,t[r++]=i>>12&63|128,t[r++]=i>>6&63|128,t[r++]=i&63|128):(t[r++]=i>>12|224,t[r++]=i>>6&63|128,t[r++]=i&63|128)}return Uint8Array.from(t)},Wo=-862048943,Go=461845907,Ko=15,uP=13,lP=5,cP=-430675100,fP=function(e,t){t===void 0&&(t=0);for(var r=aP(e),n=r.length,i=n>>2,o=t,s=0;s>>0},dP=function(e,t){var r=e,n=t;return r=Math.imul(r,Wo),r=Fi(r,Ko),r=Math.imul(r,Go),n^=r,n=Fi(n,uP),n=Math.imul(n,lP),n+cP|0},hP=function(e){var t=e;return t^=t>>>16,t=Math.imul(t,-2048144789),t^=t>>>13,t=Math.imul(t,-1028477387),t^=t>>>16,t},Fi=function(e,t,r){r===void 0&&(r=32),t>r&&(t=t%r);var n=4294967295<>>0,i=(e&n)>>>0>>>r-t>>>0;return(e<>>0},vP=function(e,t){t===void 0&&(t=0);var r=e[t]<<24|e[t+1]<<16|e[t+2]<<8|e[t+3];return pP(r)},pP=function(e){return(e&-16777216)>>>24|(e&16711680)>>>8|(e&65280)<<8|(e&255)<<24},rh=function(e,t){var r,n;if(!(!t||t.length===0)){try{for(var i=kt(t),o=i.next();!o.done;o=i.next()){var s=o.value;if(!s||!e||typeof e!="object")return;e=e[s]}}catch(a){r={error:a}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}if(e!=null)return e}},gP="(\\d+)\\.(\\d+)",mP="(\\d+)",yP="(-(([-\\w]+\\.?)*))?",bP="^".concat(gP,"(\\.").concat(mP).concat(yP,")?$"),EP=(function(){function e(t,r,n,i){i===void 0&&(i=void 0),this.major=t,this.minor=r,this.patch=n,this.preRelease=i}return e.parse=function(t){if(t){var r=new RegExp(bP).exec(t);if(r){var n=Number(r[1]),i=Number(r[2]);if(!(isNaN(n)||isNaN(i))){var o=Number(r[4])||0,s=r[5]||void 0;return new e(n,i,o,s)}}}},e.prototype.compareTo=function(t){return this.major>t.major?1:this.majort.minor?1:this.minort.patch?1:this.patcht.preRelease?1:this.preRelease=p&&l=y&&f<_)return g.variant}}catch(S){o={error:S}}finally{try{m&&!m.done&&(s=E.return)&&s.call(E)}finally{if(o)throw o.error}}}}catch(S){n={error:S}}finally{try{h&&!h.done&&(i=d.return)&&i.call(d)}finally{if(n)throw n.error}}return r.variant},e.prototype.matchNull=function(t,r){var n=this.containsNone(r);switch(t){case se.IS:case se.CONTAINS:case se.LESS_THAN:case se.LESS_THAN_EQUALS:case se.GREATER_THAN:case se.GREATER_THAN_EQUALS:case se.VERSION_LESS_THAN:case se.VERSION_LESS_THAN_EQUALS:case se.VERSION_GREATER_THAN:case se.VERSION_GREATER_THAN_EQUALS:case se.SET_IS:case se.SET_CONTAINS:case se.SET_CONTAINS_ANY:return n;case se.IS_NOT:case se.DOES_NOT_CONTAIN:case se.SET_DOES_NOT_CONTAIN:case se.SET_DOES_NOT_CONTAIN_ANY:return!n;default:return!1}},e.prototype.matchSet=function(t,r,n){switch(r){case se.SET_IS:return this.setEquals(t,n);case se.SET_IS_NOT:return!this.setEquals(t,n);case se.SET_CONTAINS:return this.matchesSetContainsAll(t,n);case se.SET_DOES_NOT_CONTAIN:return!this.matchesSetContainsAll(t,n);case se.SET_CONTAINS_ANY:return this.matchesSetContainsAny(t,n);case se.SET_DOES_NOT_CONTAIN_ANY:return!this.matchesSetContainsAny(t,n);default:return!1}},e.prototype.matchString=function(t,r,n){var i=this;switch(r){case se.IS:return this.matchesIs(t,n);case se.IS_NOT:return!this.matchesIs(t,n);case se.CONTAINS:return this.matchesContains(t,n);case se.DOES_NOT_CONTAIN:return!this.matchesContains(t,n);case se.LESS_THAN:case se.LESS_THAN_EQUALS:case se.GREATER_THAN:case se.GREATER_THAN_EQUALS:return this.matchesComparable(t,r,n,function(o){return i.parseNumber(o)},this.comparator);case se.VERSION_LESS_THAN:case se.VERSION_LESS_THAN_EQUALS:case se.VERSION_GREATER_THAN:case se.VERSION_GREATER_THAN_EQUALS:return this.matchesComparable(t,r,n,function(o){return EP.parse(o)},this.versionComparator);case se.REGEX_MATCH:return this.matchesRegex(t,n);case se.REGEX_DOES_NOT_MATCH:return!this.matchesRegex(t,n);default:return!1}},e.prototype.matchesIs=function(t,r){if(this.containsBooleans(r)){var n=t.toLowerCase();if(n==="true"||n==="false")return r.some(function(i){return i.toLowerCase()===n})}return r.some(function(i){return t===i})},e.prototype.matchesContains=function(t,r){var n,i;try{for(var o=kt(r),s=o.next();!s.done;s=o.next()){var a=s.value;if(t.toLowerCase().includes(a.toLowerCase()))return!0}}catch(u){n={error:u}}finally{try{s&&!s.done&&(i=o.return)&&i.call(o)}finally{if(n)throw n.error}}return!1},e.prototype.matchesComparable=function(t,r,n,i,o){var s=this,a=i(t),u=n.map(function(c){return i(c)}).filter(function(c){return c!==void 0});return a===void 0||u.length===0?n.some(function(c){return s.comparator(t,r,c)}):u.some(function(c){return o(a,r,c)})},e.prototype.comparator=function(t,r,n){switch(r){case se.LESS_THAN:case se.VERSION_LESS_THAN:return tn;case se.GREATER_THAN_EQUALS:case se.VERSION_GREATER_THAN_EQUALS:return t>=n;default:return!1}},e.prototype.versionComparator=function(t,r,n){var i=t.compareTo(n);switch(r){case se.LESS_THAN:case se.VERSION_LESS_THAN:return i<0;case se.LESS_THAN_EQUALS:case se.VERSION_LESS_THAN_EQUALS:return i<=0;case se.GREATER_THAN:case se.VERSION_GREATER_THAN:return i>0;case se.GREATER_THAN_EQUALS:case se.VERSION_GREATER_THAN_EQUALS:return i>=0;default:return!1}},e.prototype.matchesRegex=function(t,r){return r.some(function(n){return!!new RegExp(n).exec(t)})},e.prototype.containsNone=function(t){return t.some(function(r){return r==="(none)"})},e.prototype.containsBooleans=function(t){return t.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},e.prototype.parseNumber=function(t){var r;return(r=Number(t))!==null&&r!==void 0?r:void 0},e.prototype.coerceString=function(t){if(t!=null)return typeof t=="object"?JSON.stringify(t):String(t)},e.prototype.coerceStringArray=function(t){var r=this;if(Array.isArray(t)){var n=t;return n.map(function(a){return r.coerceString(a)}).filter(Boolean)}var i=String(t);try{var o=JSON.parse(i);if(Array.isArray(o)){var n=t;return n.map(function(u){return r.coerceString(u)}).filter(Boolean)}else{var s=this.coerceString(i);return s?[s]:void 0}}catch{var s=this.coerceString(i);return s?[s]:void 0}},e.prototype.isSetOperator=function(t){switch(t){case se.SET_IS:case se.SET_IS_NOT:case se.SET_CONTAINS:case se.SET_DOES_NOT_CONTAIN:case se.SET_CONTAINS_ANY:case se.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},e.prototype.setEquals=function(t,r){var n=new Set(t),i=new Set(r);return n.size===i.size&&$l([],Vl(i),!1).every(function(o){return n.has(o)})},e.prototype.matchesSetContainsAll=function(t,r){var n,i;if(t.length{let t={};return e.forEach((r,n)=>t[r]=n),t})(_i),AP=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,rt=String.fromCharCode.bind(String),oh=typeof Uint8Array.from=="function"?Uint8Array.from.bind(Uint8Array):e=>new Uint8Array(Array.prototype.slice.call(e,0)),Om=e=>e.replace(/=/g,"").replace(/[+\/]/g,t=>t=="+"?"-":"_"),xm=e=>e.replace(/[^A-Za-z0-9\+\/]/g,""),Nm=e=>{let t,r,n,i,o="";const s=e.length%3;for(let a=0;a255||(n=e.charCodeAt(a++))>255||(i=e.charCodeAt(a++))>255)throw new TypeError("invalid character found");t=r<<16|n<<8|i,o+=_i[t>>18&63]+_i[t>>12&63]+_i[t>>6&63]+_i[t&63]}return s?o.slice(0,s-3)+"===".substring(s):o},Wl=typeof btoa=="function"?e=>btoa(e):ri?e=>Buffer.from(e,"binary").toString("base64"):Nm,Gu=ri?e=>Buffer.from(e).toString("base64"):e=>{let r=[];for(let n=0,i=e.length;nt?Om(Gu(e)):Gu(e),IP=e=>{if(e.length<2){var t=e.charCodeAt(0);return t<128?e:t<2048?rt(192|t>>>6)+rt(128|t&63):rt(224|t>>>12&15)+rt(128|t>>>6&63)+rt(128|t&63)}else{var t=65536+(e.charCodeAt(0)-55296)*1024+(e.charCodeAt(1)-56320);return rt(240|t>>>18&7)+rt(128|t>>>12&63)+rt(128|t>>>6&63)+rt(128|t&63)}},CP=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,Lm=e=>e.replace(CP,IP),sh=ri?e=>Buffer.from(e,"utf8").toString("base64"):ih?e=>Gu(ih.encode(e)):e=>Wl(Lm(e)),Hn=(e,t=!1)=>t?Om(sh(e)):sh(e),ah=e=>Hn(e,!0),kP=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,RP=e=>{switch(e.length){case 4:var t=(7&e.charCodeAt(0))<<18|(63&e.charCodeAt(1))<<12|(63&e.charCodeAt(2))<<6|63&e.charCodeAt(3),r=t-65536;return rt((r>>>10)+55296)+rt((r&1023)+56320);case 3:return rt((15&e.charCodeAt(0))<<12|(63&e.charCodeAt(1))<<6|63&e.charCodeAt(2));default:return rt((31&e.charCodeAt(0))<<6|63&e.charCodeAt(1))}},Mm=e=>e.replace(kP,RP),Dm=e=>{if(e=e.replace(/\s+/g,""),!AP.test(e))throw new TypeError("malformed base64.");e+="==".slice(2-(e.length&3));let t,r="",n,i;for(let o=0;o>16&255):i===64?rt(t>>16&255,t>>8&255):rt(t>>16&255,t>>8&255,t&255);return r},Gl=typeof atob=="function"?e=>atob(xm(e)):ri?e=>Buffer.from(e,"base64").toString("binary"):Dm,Um=ri?e=>oh(Buffer.from(e,"base64")):e=>oh(Gl(e).split("").map(t=>t.charCodeAt(0))),Fm=e=>Um(Hm(e)),PP=ri?e=>Buffer.from(e,"base64").toString("utf8"):nh?e=>nh.decode(Um(e)):e=>Mm(Gl(e)),Hm=e=>xm(e.replace(/[-_]/g,t=>t=="-"?"+":"/")),Ku=e=>PP(Hm(e)),OP=e=>{if(typeof e!="string")return!1;const t=e.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(t)||!/[^\s0-9a-zA-Z\-_]/.test(t)},Bm=e=>({value:e,enumerable:!1,writable:!0,configurable:!0}),jm=function(){const e=(t,r)=>Object.defineProperty(String.prototype,t,Bm(r));e("fromBase64",function(){return Ku(this)}),e("toBase64",function(t){return Hn(this,t)}),e("toBase64URI",function(){return Hn(this,!0)}),e("toBase64URL",function(){return Hn(this,!0)}),e("toUint8Array",function(){return Fm(this)})},qm=function(){const e=(t,r)=>Object.defineProperty(Uint8Array.prototype,t,Bm(r));e("toBase64",function(t){return zo(this,t)}),e("toBase64URI",function(){return zo(this,!0)}),e("toBase64URL",function(){return zo(this,!0)})},xP=()=>{jm(),qm()},zu={version:Pm,VERSION:wP,atob:Gl,atobPolyfill:Dm,btoa:Wl,btoaPolyfill:Nm,fromBase64:Ku,toBase64:Hn,encode:Hn,encodeURI:ah,encodeURL:ah,utob:Lm,btou:Mm,decode:Ku,isValid:OP,fromUint8Array:zo,toUint8Array:Fm,extendString:jm,extendUint8Array:qm,extendBuiltins:xP};var Vm=(function(e){Im(t,e);function t(r,n){var i=e.call(this,n)||this;return i.statusCode=r,Object.setPrototypeOf(i,t.prototype),i}return t})(Error),Xu=(function(e){Im(t,e);function t(r){var n=e.call(this,r)||this;return Object.setPrototypeOf(n,t.prototype),n}return t})(Error),NP=(function(){function e(t,r,n){this.deploymentKey=t,this.serverUrl=r,this.httpClient=n}return e.prototype.getVariants=function(t,r){return Cm(this,void 0,void 0,function(){var n,i,o,s;return km(this,function(a){switch(a.label){case 0:return n=zu.encodeURL(JSON.stringify(t)),i={Authorization:"Api-Key ".concat(this.deploymentKey),"X-Amp-Exp-User":n},r?.flagKeys&&(i["X-Amp-Exp-Flag-Keys"]=zu.encodeURL(JSON.stringify(r.flagKeys))),r?.trackingOption&&(i["X-Amp-Exp-Track"]=r.trackingOption),r?.exposureTrackingOption&&(i["X-Amp-Exp-Exposure-Track"]=r.exposureTrackingOption),o=new URL("".concat(this.serverUrl,"/sdk/v2/vardata?v=0")),r?.evaluationMode&&o.searchParams.append("eval_mode",r?.evaluationMode),r?.deliveryMethod&&o.searchParams.append("delivery_method",r?.deliveryMethod),[4,this.httpClient.request({requestUrl:o.toString(),method:"GET",headers:i,timeoutMillis:r?.timeoutMillis})];case 1:if(s=a.sent(),s.status!=200)throw new Vm(s.status,"Fetch error response: status=".concat(s.status));return[2,JSON.parse(s.body)]}})})},e})(),LP=(function(){function e(t,r,n){this.deploymentKey=t,this.serverUrl=r,this.httpClient=n}return e.prototype.getFlags=function(t){return Cm(this,void 0,void 0,function(){var r,n,i;return km(this,function(o){switch(o.label){case 0:return r={Authorization:"Api-Key ".concat(this.deploymentKey)},t?.libraryName&&t?.libraryVersion&&(r["X-Amp-Exp-Library"]="".concat(t.libraryName,"/").concat(t.libraryVersion)),t?.user&&(r["X-Amp-Exp-User"]=zu.encodeURL(JSON.stringify(t.user))),[4,this.httpClient.request({requestUrl:"".concat(this.serverUrl,"/sdk/v2/flags")+(t?.deliveryMethod?"?delivery_method=".concat(t.deliveryMethod):""),method:"GET",headers:r,timeoutMillis:t?.timeoutMillis})];case 1:if(n=o.sent(),n.status!=200)throw Error("Flags error response: status=".concat(n.status));return i=JSON.parse(n.body),[2,i.reduce(function(s,a){return s[a.key]=a,s},{})]}})})},e})(),Ge=typeof globalThis<"u"?globalThis:global||self,hn=function(){if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},MP=function(){var e=hn();if(e)try{var t="EXP_test";return e.localStorage.setItem(t,t),e.localStorage.removeItem(t),!0}catch{return!1}return!1},DP=(function(){function e(t,r){this.poller=void 0,this.action=t,this.ms=r}return e.prototype.start=function(){this.poller||(this.poller=Ge.setInterval(this.action,this.ms),this.action())},e.prototype.stop=function(){this.poller&&(Ge.clearInterval(this.poller),this.poller=void 0)},e})(),Si={exports:{}},UP=Si.exports,uh;function FP(){return uh||(uh=1,(function(e,t){(function(r,n){var i="0.7.33",o="",s="?",a="function",u="undefined",c="object",l="string",f="major",d="model",h="name",v="type",p="vendor",b="version",E="architecture",m="console",g="mobile",y="tablet",_="smarttv",S="wearable",A="embedded",I=350,C="Amazon",R="Apple",P="ASUS",k="BlackBerry",N="Browser",$="Chrome",ee="Edge",B="Firefox",z="Google",K="Huawei",Q="LG",ae="Microsoft",fe="Motorola",pe="Opera",ye="Samsung",be="Sharp",de="Sony",me="Xiaomi",M="Zebra",Z="Facebook",X=function(W,j){var J={};for(var ne in W)j[ne]&&j[ne].length%2===0?J[ne]=j[ne].concat(W[ne]):J[ne]=W[ne];return J},re=function(W){for(var j={},J=0;J0?ue.length===2?typeof ue[1]==a?this[ue[0]]=ue[1].call(this,Oe):this[ue[0]]=ue[1]:ue.length===3?typeof ue[1]===a&&!(ue[1].exec&&ue[1].test)?this[ue[0]]=Oe?ue[1].call(this,Oe,ue[2]):n:this[ue[0]]=Oe?Oe.replace(ue[1],ue[2]):n:ue.length===4&&(this[ue[0]]=Oe?ue[3].call(this,Oe.replace(ue[1],ue[2])):n):this[ue]=Oe||n;J+=2}},H=function(W,j){for(var J in j)if(typeof j[J]===c&&j[J].length>0){for(var ne=0;neI?T(ie,I):ie,this},this.setUA(J),this};G.VERSION=i,G.BROWSER=re([h,b,f]),G.CPU=re([E]),G.DEVICE=re([d,p,v,m,g,_,y,S,A]),G.ENGINE=G.OS=re([h,b]),e.exports&&(t=e.exports=G),t.UAParser=G;var Y=typeof r!==u&&(r.jQuery||r.Zepto);if(Y&&!Y.ua){var V=new G;Y.ua=V.getResult(),Y.ua.get=function(){return V.getUA()},Y.ua.set=function(W){V.setUA(W);var j=V.getResult();for(var J in j)Y.ua[J]=j[J]}}})(typeof window=="object"?window:UP)})(Si,Si.exports)),Si.exports}var HP=FP(),Te=function(){return Te=Object.assign||function(t){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!o||c[1]>o[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function at(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}function Pt(e,t,r){if(arguments.length===2)for(var n=0,i=t.length,o;n=2&&v[1]&&(p=atob(v[1])),{deviceId:v[0],userId:p}}catch{return}},BP=function(e){var t=Kl(e,!0);try{var r=Ge.localStorage.getItem(t);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},jP=function(e){var t=Kl(e,!0);try{var r=Ge.sessionStorage.getItem(t);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},Kl=function(e,t){if(t)return e?.length<10?void 0:"AMP_".concat(e.substring(0,10));if(!(e?.length<6))return"amp_".concat(e.substring(0,6))},qP=(function(){function e(t,r,n){this.type="integration",this.apiKey=t,this.identityStore=r.identityStore,this.eventBridge=r.eventBridge,this.contextProvider=r.applicationContextProvider,this.timeoutMillis=n,this.loadPersistedState(),n<=0&&(this.setup=void 0)}return e.prototype.setup=function(t,r){return Qe(this,void 0,void 0,function(){return Ze(this,function(n){return t?.automaticFetchOnAmplitudeIdentityChange&&this.identityStore.addIdentityListener(function(){r?.fetch()}),[2,this.waitForConnectorIdentity(this.timeoutMillis)]})})},e.prototype.getUser=function(){var t=this.identityStore.getIdentity();return{user_id:t.userId,device_id:t.deviceId,user_properties:t.userProperties,version:this.contextProvider.versionName}},e.prototype.track=function(t){return this.eventBridge.receiver?(this.eventBridge.logEvent({eventType:t.eventType,eventProperties:t.eventProperties}),!0):!1},e.prototype.loadPersistedState=function(){if(!this.apiKey||this.apiKey.startsWith("client-"))return!1;var t=lh(this.apiKey,!0);return t?(this.commitIdentityToConnector(t),!0):(t=lh(this.apiKey,!1),t?(this.commitIdentityToConnector(t),!0):(t=BP(this.apiKey),t?(this.commitIdentityToConnector(t),!0):(t=jP(this.apiKey),t?(this.commitIdentityToConnector(t),!0):!1)))},e.prototype.commitIdentityToConnector=function(t){var r=this.identityStore.editIdentity();r.setDeviceId(t.deviceId),t.userId&&r.setUserId(t.userId),r.commit()},e.prototype.waitForConnectorIdentity=function(t){return Qe(this,void 0,void 0,function(){var r,n=this;return Ze(this,function(i){return r=this.identityStore.getIdentity(),!r.userId&&!r.deviceId?[2,Promise.race([new Promise(function(o){var s=function(){o(),n.identityStore.removeIdentityListener(s)};n.identityStore.addIdentityListener(s)}),new Promise(function(o,s){Ge.setTimeout(s,t,"Timed out waiting for Amplitude Analytics SDK to initialize.")})])]:[2]})})},e})();function VP(e,t){return t=t||{},new Promise(function(r,n){var i=new XMLHttpRequest,o=[],s=[],a={},u=function(){return{ok:(i.status/100|0)==2,statusText:i.statusText,status:i.status,url:i.responseURL,text:function(){return Promise.resolve(i.responseText)},json:function(){return Promise.resolve(JSON.parse(i.responseText))},blob:function(){return Promise.resolve(new Blob([i.response]))},clone:u,headers:{keys:function(){return o},entries:function(){return s},get:function(l){return a[l.toLowerCase()]},has:function(l){return l.toLowerCase()in a}}}};for(var c in i.open(t.method||"get",e,!0),i.onload=function(){i.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,function(l,f,d){o.push(f=f.toLowerCase()),s.push([f,d]),a[f]=a[f]?a[f]+","+d:d}),r(u())},i.onerror=n,i.withCredentials=t.credentials=="include",t.headers)i.setRequestHeader(c,t.headers[c]);i.send(t.body||null)})}var $P=Ge.fetch||VP,WP=function(e,t){return t==null||t<=0?e:new Promise(function(r,n){Ge.setTimeout(function(){n(new Xu("Request timeout after "+t+" milliseconds"))},t),e.then(r,n)})},GP=function(e,t,r,n,i){var o=function(){return Qe(void 0,void 0,void 0,function(){var s,a,u;return Ze(this,function(c){switch(c.label){case 0:return[4,$P(e,{method:t,headers:r,body:n})];case 1:return s=c.sent(),u={status:s.status},[4,s.text()];case 2:return a=(u.body=c.sent(),u),[2,a]}})})};return WP(o(),i)},KP=(function(){function e(t){this.client=t}return e.prototype.request=function(t){return Qe(this,void 0,void 0,function(){return Ze(this,function(r){switch(r.label){case 0:return[4,this.client.request(t.requestUrl,t.method,t.headers,null,t.timeoutMillis)];case 1:return[2,r.sent()]}})})},e})(),$m={request:GP},Ft;(function(e){e[e.Disable=0]="Disable",e[e.Error=1]="Error",e[e.Warn=2]="Warn",e[e.Info=3]="Info",e[e.Debug=4]="Debug",e[e.Verbose=5]="Verbose"})(Ft||(Ft={}));var lr;(function(e){e.LocalStorage="localStorage",e.InitialVariants="initialVariants"})(lr||(lr={}));var ze;(function(e){e.LocalStorage="storage",e.InitialVariants="initial",e.SecondaryLocalStorage="secondary-storage",e.SecondaryInitialVariants="secondary-initial",e.FallbackInline="fallback-inline",e.FallbackConfig="fallback-config",e.LocalEvaluation="local-evaluation"})(ze||(ze={}));var ch=function(e){return!e||e===ze.FallbackInline||e===ze.FallbackConfig||e===ze.SecondaryInitialVariants},In={debug:!1,logLevel:Ft.Error,loggerProvider:null,instanceName:"$default_instance",fallbackVariant:{},initialVariants:{},initialFlags:void 0,source:lr.LocalStorage,serverUrl:"https://api.lab.amplitude.com",flagsServerUrl:"https://flag.lab.amplitude.com",serverZone:"US",fetchTimeoutMillis:1e4,retryFetchOnFailure:!0,throwOnError:!1,automaticExposureTracking:!0,pollOnStart:!0,flagConfigPollingIntervalMillis:3e5,fetchOnStart:!0,automaticFetchOnAmplitudeIdentityChange:!1,userProvider:null,analyticsProvider:null,exposureTrackingProvider:null,httpClient:$m},fh="1.20.4",zP=512,XP=(function(){function e(t,r){var n=this,i;this.isReady=new Promise(function(s){n.resolve=s}),this.config=t,this.client=r;var o=(i=t.instanceName)!==null&&i!==void 0?i:In.instanceName;this.queue=new JP(o),this.cache=new YP(o)}return e.prototype.ready=function(){return this.integration?this.isReady:Promise.resolve()},e.prototype.setIntegration=function(t){var r=this;this.integration&&this.integration.teardown&&this.integration.teardown(),this.integration=t,t.setup?this.integration.setup(this.config,this.client).then(function(){r.queue.setTracker(r.integration.track.bind(t)),r.resolve()},function(){r.queue.setTracker(r.integration.track.bind(t)),r.resolve()}):(this.queue.setTracker(this.integration.track.bind(t)),this.resolve())},e.prototype.getUser=function(){return this.integration?this.integration.getUser():{}},e.prototype.track=function(t,r){if(this.cache.shouldTrack(t,r)){var n=this.getExposureEvent(t);this.queue.push(n)}},e.prototype.getExposureEvent=function(t){var r,n,i,o={eventType:"$exposure",eventProperties:t};return!((r=t.metadata)===null||r===void 0)&&r.exposureEvent?o={eventType:(n=t.metadata)===null||n===void 0?void 0:n.exposureEvent,eventProperties:t}:((i=t.metadata)===null||i===void 0?void 0:i.deliveryMethod)==="web"&&(o={eventType:"$impression",eventProperties:t}),o},e})(),YP=(function(){function e(t){this.isSessionStorageAvailable=QP(),this.inMemoryCache={},this.identity={},this.storageKey="EXP_sent_v3_".concat(t),this.isSessionStorageAvailable&&(Ge.sessionStorage.removeItem("EXP_sent_".concat(t)),Ge.sessionStorage.removeItem("EXP_sent_v2_".concat(t)))}return e.prototype.shouldTrack=function(t,r){var n,i;if(((n=t.metadata)===null||n===void 0?void 0:n.deliveryMethod)==="web")return!0;var o={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,o)||this.clearCache(),this.identity=o,this.loadCache();var s=t.flag_key in this.inMemoryCache,a=this.inMemoryCache[t.flag_key],u=(i=t.variant)!==null&&i!==void 0?i:null,c=a??null,l=!1;return(!s||c!==u)&&(l=!0,this.inMemoryCache[t.flag_key]=u),this.storeCache(),l},e.prototype.clearCache=function(){this.inMemoryCache={},this.isSessionStorageAvailable&&Ge.sessionStorage.removeItem(this.storageKey)},e.prototype.identityEquals=function(t,r){return t.userId&&r.userId?t.userId===r.userId:!t.userId&&!r.userId?t.deviceId===r.deviceId:!1},e.prototype.loadCache=function(){if(this.isSessionStorageAvailable){var t=Ge.sessionStorage.getItem(this.storageKey);this.inMemoryCache=t?JSON.parse(t):{}}},e.prototype.storeCache=function(){if(this.isSessionStorageAvailable)try{Ge.sessionStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryCache))}catch{}},e})(),JP=(function(){function e(t,r){r===void 0&&(r=zP),this.isLocalStorageAvailable=MP(),this.inMemoryQueue=[],this.storageKey="EXP_unsent_".concat(t),this.maxQueueSize=r}return e.prototype.push=function(t){this.loadQueue(),this.inMemoryQueue.push(t),this.flush(),this.storeQueue()},e.prototype.setTracker=function(t){var r=this;this.tracker=t,this.poller=Ge.setInterval(function(){r.loadFlushStore()},1e3),this.loadFlushStore()},e.prototype.flush=function(){if(this.tracker&&this.inMemoryQueue.length!==0){for(var t=0;tthis.maxQueueSize&&(this.inMemoryQueue=this.inMemoryQueue.slice(this.inMemoryQueue.length-this.maxQueueSize)),Ge.localStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryQueue)))},e.prototype.loadFlushStore=function(){this.loadQueue(),this.flush(),this.storeQueue()},e})(),QP=function(){var e=hn();if(e)try{var t="EXP_test";return e.sessionStorage.setItem(t,t),e.sessionStorage.removeItem(t),!0}catch{return!1}return!1},ZP=(function(){function e(t,r){r===void 0&&(r=Ft.Error),this.logger=t,this.logLevel=r}return e.prototype.error=function(t){for(var r,n=[],i=1;i=Ft.Error&&(r=this.logger).error.apply(r,Pt([t],at(n),!1))},e.prototype.warn=function(t){for(var r,n=[],i=1;i=Ft.Warn&&(r=this.logger).warn.apply(r,Pt([t],at(n),!1))},e.prototype.info=function(t){for(var r,n=[],i=1;i=Ft.Info&&(r=this.logger).info.apply(r,Pt([t],at(n),!1))},e.prototype.debug=function(t){for(var r,n=[],i=1;i=Ft.Debug&&(r=this.logger).debug.apply(r,Pt([t],at(n),!1))},e.prototype.verbose=function(t){for(var r,n=[],i=1;i=Ft.Verbose&&(r=this.logger).verbose.apply(r,Pt([t],at(n),!1))},e})(),eO=(function(){function e(){}return e.prototype.error=function(t){for(var r=[],n=1;n0&&f[0]){var d=f[0],h={group_name:d},v=(i=(n=e.group_properties)===null||n===void 0?void 0:n[l])===null||i===void 0?void 0:i[d];v&&Object.keys(v).length>0&&(h.group_properties=v),a[l]=h}}}catch(p){t={error:p}}finally{try{c&&!c.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}return Object.keys(a).length>0&&(o.groups=a),delete o.user.groups,delete o.user.group_properties,o},ut=function(e){return e==null?{}:typeof e=="string"?{key:e,value:e}:e},hh=function(e){if(!e)return{};var t=void 0;e.metadata&&(t=e.metadata.experimentKey);var r={};return e.key&&(r.key=e.key),e.value&&(r.value=e.value),e.payload&&(r.payload=e.payload),t&&(r.expKey=t),e.metadata&&(r.metadata=e.metadata),r},lO=(function(){function e(t){this.setProperties={},this.unsetProperties={},this.analyticsProvider=t}return e.prototype.track=function(t){this.setProperties[t.key]!=t.variant.value&&(this.setProperties[t.key]=t.variant.value,delete this.unsetProperties[t.key],this.analyticsProvider.track(t))},e.prototype.setUserProperty=function(t){this.setProperties[t.key]!=t.variant.value&&this.analyticsProvider.setUserProperty(t)},e.prototype.unsetUserProperty=function(t){this.unsetProperties[t.key]||(this.unsetProperties[t.key]="unset",delete this.setProperties[t.key],this.analyticsProvider.unsetUserProperty(t))},e})(),cO=(function(){function e(t){this.tracked={},this.identity={},this.exposureTrackingProvider=t}return e.prototype.track=function(t,r){var n={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,n)||(this.tracked={}),this.identity=n;var i=t.flag_key in this.tracked,o=this.tracked[t.flag_key];i&&o===t.variant||(this.tracked[t.flag_key]=t.variant,this.exposureTrackingProvider.track(t))},e.prototype.identityEquals=function(t,r){return t.userId===r.userId&&t.deviceId===r.deviceId},e})(),fO=1e4,dO=8,hO=500,vO=1e4,pO=1.5,vh=6e4,gO="https://api.lab.eu.amplitude.com",mO="https://flag.lab.eu.amplitude.com",yO=(function(){function e(t,r){var n=this,i,o,s,a;this.engine=new _P,this.isRunning=!1,this.apiKey=t,r=sO(r),this.config=Te(Te(Te({},In),r),{serverUrl:r?.serverUrl||(((i=r?.serverZone)===null||i===void 0?void 0:i.toLowerCase())==="eu"?gO:In.serverUrl),flagsServerUrl:r?.flagsServerUrl||(((o=r?.serverZone)===null||o===void 0?void 0:o.toLowerCase())==="eu"?mO:In.flagsServerUrl),flagConfigPollingIntervalMillis:r.flagConfigPollingIntervalMillis=500||t.statusCode===429:!0},e.prototype.addPlugin=function(t){t.type==="integration"&&this.integrationManager.setIntegration(t)},e})(),bO=(function(){function e(t,r){var n,i,o;this.globalScope=hn(),this.userAgent=typeof((n=this.globalScope)===null||n===void 0?void 0:n.navigator)<"u"?(i=this.globalScope)===null||i===void 0?void 0:i.navigator.userAgent:void 0,this.ua=new HP.UAParser(this.userAgent).getResult(),this.localStorage=new Gs,this.sessionStorage=new Km,this.userProvider=t,this.apiKey=r,this.storageKey="EXP_".concat((o=this.apiKey)===null||o===void 0?void 0:o.slice(0,10),"_DEFAULT_USER_PROVIDER")}return e.prototype.getUser=function(){var t,r,n,i,o,s=((t=this.userProvider)===null||t===void 0?void 0:t.getUser())||{};return Te({language:this.getLanguage(),platform:"Web",os:this.getOs(this.ua),device_model:this.getDeviceModel(this.ua),device_category:(n=(r=this.ua.device)===null||r===void 0?void 0:r.type)!==null&&n!==void 0?n:"desktop",referring_url:(o=(i=this.globalScope)===null||i===void 0?void 0:i.document)===null||o===void 0?void 0:o.referrer.replace(/\/$/,""),cookie:this.getCookie(),browser:this.getBrowser(this.ua),landing_url:this.getLandingUrl(),first_seen:this.getFirstSeen(),url_param:this.getUrlParam(),user_agent:this.userAgent},s)},e.prototype.getLanguage=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},e.prototype.getOs=function(t){var r,n;return[(r=t.browser)===null||r===void 0?void 0:r.name,(n=t.browser)===null||n===void 0?void 0:n.major].filter(function(i){return i!=null}).join(" ")},e.prototype.getDeviceModel=function(t){var r;return(r=t.os)===null||r===void 0?void 0:r.name},e.prototype.getBrowser=function(t){var r,n=(r=t.browser)===null||r===void 0?void 0:r.name;return n?.includes("Chrom")&&(n="Chrome"),n?.includes("Firefox")&&(n="Firefox"),n?.includes("Safari")&&(n="Safari"),n?.includes("Edge")&&(n="Edge"),n?.includes("Opera")&&(n="Opera"),n},e.prototype.getCookie=function(){var t,r,n,i,o;if(!((r=(t=this.globalScope)===null||t===void 0?void 0:t.document)===null||r===void 0)&&r.cookie)return Object.fromEntries((o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.document)===null||i===void 0?void 0:i.cookie)===null||o===void 0?void 0:o.split("; ").map(function(s){return s.split("=")}))},e.prototype.getLandingUrl=function(){var t,r;try{var n=JSON.parse(this.sessionStorage.get(this.storageKey)||"{}");return n.landing_url||(n.landing_url=(r=(t=this.globalScope)===null||t===void 0?void 0:t.location)===null||r===void 0?void 0:r.href.replace(/\/$/,""),this.sessionStorage.put(this.storageKey,JSON.stringify(n))),n.landing_url}catch{return}},e.prototype.getFirstSeen=function(){try{var t=JSON.parse(this.localStorage.get(this.storageKey)||"{}");return t.first_seen||(t.first_seen=(Date.now()/1e3).toString(),this.localStorage.put(this.storageKey,JSON.stringify(t))),t.first_seen}catch{return}},e.prototype.getUrlParam=function(){var t,r,n;if(this.globalScope){var i={};try{var o=new URL(this.globalScope.location.href);try{for(var s=Dr(o.searchParams),a=s.next();!a.done;a=s.next()){var u=at(a.value,2),c=u[0],l=u[1];i[c]=Pt(Pt([],at((n=i[c])!==null&&n!==void 0?n:[]),!1),at(l.split(",")),!1)}}catch(f){t={error:f}}finally{try{a&&!a.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}}catch{return}return Object.entries(i).reduce(function(f,d){var h=at(d,2),v=h[0],p=h[1];return f[v]=p.length==1?p[0]:p,f},{})}},e})();Ge.experimentInstances={};var ph=Ge.experimentInstances,EO=function(e,t){var r=function(){return new qP(e,Al.getInstance(zm(t)),1e4)};return wO(e,t,r)},zm=function(e){return e?.instanceName||In.instanceName},_O=function(e,t){var r=zm(t),n=t?.internalInstanceNameSuffix;return n?"".concat(r,".").concat(e,".").concat(n):"".concat(r,".").concat(e)},SO=function(e,t){return new yO(e,Te(Te({},t),{userProvider:new bO(t?.userProvider,e)}))},wO=function(e,t,r){var n=_O(e,t),i=ph[n];return i||(i=SO(e,t),r&&i.addPlugin(r()),ph[n]=i,i)},Xo=(function(){function e(t){this.name=e.pluginName,this.config=t}return e.prototype.setup=function(t,r){var n;return x(this,void 0,void 0,function(){return O(this,function(i){return this.experiment=EO(((n=this.config)===null||n===void 0?void 0:n.deploymentKey)||t.apiKey,this.config),[2]})})},e.pluginName="@amplitude/experiment-analytics-plugin",e})(),TO=function(e){return new Xo(e)},AO=(function(){function e(){}return e.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:IO(),platform:"Web",os:void 0,deviceModel:void 0}},e})(),IO=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},CO=(function(){function e(){this.queue=[]}return e.prototype.logEvent=function(t){this.receiver?this.receiver(t):this.queue.length<512&&this.queue.push(t)},e.prototype.setEventReceiver=function(t){this.receiver=t,this.queue.length>0&&(this.queue.forEach(function(r){t(r)}),this.queue=[])},e})(),Pr=function(){return Pr=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function gh(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),i,o=[],s;try{for(;(t===void 0||t-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(a){s={error:a}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(s)throw s.error}}return o}var Jo=function(e,t){var r,n,i=["string","number","boolean","undefined"],o=typeof e,s=typeof t;if(o!==s)return!1;try{for(var a=Yo(i),u=a.next();!u.done;u=a.next()){var c=u.value;if(c===o)return e===t}}catch(b){r={error:b}}finally{try{u&&!u.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}if(e==null&&t==null)return!0;if(e==null||t==null||e.length!==t.length)return!1;var l=Array.isArray(e),f=Array.isArray(t);if(l!==f)return!1;if(l&&f){for(var d=0;d{if(f=!0,_&&(clearTimeout(_),_=null),d._q&&d._q.length>0)for(console.warn(`Engagement SDK failed to load within ${A}ms. Resolving pending calls gracefully.`);d._q.length>0;){let I=d._q.shift();if(!I)continue;let C=I[0],R=mh.includes(C);if(console.warn(`Engagement SDK method '${C}' still in queue (isAsyncMethod=${R}); attempting to resolve as no-op.`),R&&I[1]instanceof Function&&I[2]instanceof Function){let P=I[1];console.warn(`Engagement SDK method '${C}' resolved as no-op due to script loading failure`),P(void 0)}}};e(y,d._configuration.options.splitting?"module":void 0,p?.nonce,S);let A=1e4;_=setTimeout(()=>{S()},A)},plugin(v){let p=d.init;return{name:"@amplitude/engagement-browser",type:"enrichment",async setup(b,E){var m;let g=(m=b.instanceName)!=null?m:NO,y=xO.getInstance(g).identityStore;p(b.apiKey,{serverZone:b.serverZone,...v,options:{logLevel:b.logLevel,logger:b.loggerProvider,...v?.options}});let _=[{track:S=>{E.track(S)}}];await window.engagement.boot({user:()=>{let S=y.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:S.userProperties,getSessionId:E.getSessionId}},integrations:_}),y.addIdentityListener(S=>{var A,I,C,R;if(!((A=window.engagement)!=null&&A._.user)||!((I=window.engagement)!=null&&I._analytics.hasBooted)){console.warn("Engagement SDK not booted. Ignoring identity change.");return}((R=(C=window.engagement)==null?void 0:C._.user)==null?void 0:R.user_id)!==S.userId?(window.engagement.shutdown(),window.engagement.boot({user:()=>{let P=y.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:P.userProperties,getSessionId:E.getSessionId}},integrations:_})):window.engagement._setUserProperties(S.userProperties)})},async execute(b){return window.engagement.forwardEvent(b),b}}}},h=d;return new Proxy(d,{get:function(v,p){if(p in h)return h[p];if(p!=="then")return p==="gs"||p==="rc"?new Proxy({},{get:function(b,E){return function(){let m=Array.from(arguments),g=`${p}.${E}`;m.unshift(g),d._q.push(m)}}}):mh.includes(p)?function(){let b=Array.prototype.slice.call(arguments);return new Promise((E,m)=>{b.unshift(p,E,m),d._q.push(b),f&&E(void 0)})}:function(){let b=Array.prototype.slice.call(arguments);b.unshift(p),d._q.push(b)}}})}var Xm=(e=>(e.Local="https://local.amplitude.com:3010/",e.Staging="https://apps.stag2.amplitude.com/",e.Production="https://app.amplitude.com/",e.ProductionEU="https://app.eu.amplitude.com/",e))(Xm||{}),MO=e=>{if(typeof window<"u"&&window.opener)for(let t of Object.values(Xm))window.opener.postMessage({message:e},t)};function DO(e,t,r,n){for(let i of e){let o=document.createElement("script");o.src=i,o.id="engagement-sdk-bundle",r&&o.setAttribute("nonce",r),t&&o.setAttribute("type",t),n&&(o.onerror=n),document.getElementsByTagName("head")[0].appendChild(o)}}var UO=()=>(MO("ENGAGEMENT_SNIPPET"),LO((e,t,r,n)=>DO([e||"https://cdn.amplitude.com/engagement-browser/prod/index.min.js.gz"],t,r,n))),yh=UO(),FO=e=>(typeof window<"u"&&!window.engagement&&(window.engagement=yh),yh.plugin(e)),HO="1.0.15",BO="amplitude-ts-unified",jO=function(){return{type:"enrichment",name:"@amplitude/unified-library-plugin",execute:function(e){var t;return x(this,void 0,void 0,function(){return O(this,function(r){return e.library="".concat(BO,"/").concat(HO,"-").concat((t=e.library)!==null&&t!==void 0?t:""),[2,e]})})}}},qO=(function(e){Mt(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.sessionReplay=function(){return this._sessionReplay},t.prototype.experiment=function(){if(this.config!==void 0){var r=this.plugins(Xo);if(r.length===0){this.config.loggerProvider.debug("".concat(Xo.pluginName," plugin is not found."));return}else{if(r.length===1)return r[0].experiment;this.config.loggerProvider.debug("Multiple instances of ".concat(Xo.pluginName," are found."));return}}},t.prototype.initAll=function(r,n){return x(this,void 0,void 0,function(){var i=this;return O(this,function(o){return this._initAllPromise?[2,this._initAllPromise]:(this._initAllPromise=(function(){return x(i,void 0,void 0,function(){var s,a;return O(this,function(u){switch(u.label){case 0:return s={serverZone:n?.serverZone,instanceName:n?.instanceName},e.prototype.add.call(this,jO()),[4,e.prototype.init.call(this,r,D(D({},n?.analytics),s)).promise];case 1:return u.sent(),[4,e.prototype.add.call(this,sP(D(D({},n?.sessionReplay),s))).promise];case 2:return u.sent(),[4,e.prototype.add.call(this,TO(D(D({},n?.experiment),s))).promise];case 3:return u.sent(),a=this.plugin($u.pluginName),a===void 0?this.config.loggerProvider.debug("".concat($u.pluginName," plugin is not found.")):this._sessionReplay=a.sessionReplay,[4,e.prototype.add.call(this,FO(D(D({},n?.engagement),s))).promise];case 4:return u.sent(),[2]}})})})().finally(function(){i._initAllPromise=void 0}),[2,this._initAllPromise])})})},t.prototype.init=function(r,n,i){r===void 0&&(r="");var o=e.prototype.init.call(this,r,n,i);return o},t})(vk),Ym=function(){var e=new qO;return{_setDiagnosticsSampleRate:_e(e._setDiagnosticsSampleRate.bind(e),"_setDiagnosticsSampleRate",Re(e),Pe(e,["config"])),_enableRequestBodyCompressionExperimental:_e(e._enableRequestBodyCompressionExperimental.bind(e),"_enableRequestBodyCompressionExperimental",Re(e),Pe(e,["config"])),experiment:_e(e.experiment.bind(e),"experiment",Re(e),Pe(e,["config"])),sessionReplay:_e(e.sessionReplay.bind(e),"sessionReplay",Re(e),Pe(e,["config"])),initAll:_e(e.initAll.bind(e),"initAll",Re(e),Pe(e,["config"])),init:_e(e.init.bind(e),"init",Re(e),Pe(e,["config"])),add:_e(e.add.bind(e),"add",Re(e),Pe(e,["config.apiKey","timeline.plugins"])),remove:_e(e.remove.bind(e),"remove",Re(e),Pe(e,["config.apiKey","timeline.plugins"])),track:_e(e.track.bind(e),"track",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),logEvent:_e(e.logEvent.bind(e),"logEvent",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),identify:_e(e.identify.bind(e),"identify",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),groupIdentify:_e(e.groupIdentify.bind(e),"groupIdentify",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),setGroup:_e(e.setGroup.bind(e),"setGroup",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),revenue:_e(e.revenue.bind(e),"revenue",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),flush:_e(e.flush.bind(e),"flush",Re(e),Pe(e,["config.apiKey","timeline.queue.length"])),getUserId:_e(e.getUserId.bind(e),"getUserId",Re(e),Pe(e,["config","config.userId"])),setUserId:_e(e.setUserId.bind(e),"setUserId",Re(e),Pe(e,["config","config.userId"])),getDeviceId:_e(e.getDeviceId.bind(e),"getDeviceId",Re(e),Pe(e,["config","config.deviceId"])),setDeviceId:_e(e.setDeviceId.bind(e),"setDeviceId",Re(e),Pe(e,["config","config.deviceId"])),reset:_e(e.reset.bind(e),"reset",Re(e),Pe(e,["config","config.userId","config.deviceId"])),getSessionId:_e(e.getSessionId.bind(e),"getSessionId",Re(e),Pe(e,["config"])),setSessionId:_e(e.setSessionId.bind(e),"setSessionId",Re(e),Pe(e,["config"])),extendSession:_e(e.extendSession.bind(e),"extendSession",Re(e),Pe(e,["config"])),setOptOut:_e(e.setOptOut.bind(e),"setOptOut",Re(e),Pe(e,["config"])),getOptOut:_e(e.getOptOut.bind(e),"getOptOut",Re(e),Pe(e,["config"])),setTransport:_e(e.setTransport.bind(e),"setTransport",Re(e),Pe(e,["config"])),getIdentity:_e(e.getIdentity.bind(e),"getIdentity",Re(e),Pe(e,["config"])),setIdentity:_e(e.setIdentity.bind(e),"setIdentity",Re(e),Pe(e,["config","config.userId","config.deviceId"]))}};const Ne=Ym();var VO=Ne._setDiagnosticsSampleRate,$O=Ne._enableRequestBodyCompressionExperimental,Jm=Ne.initAll,WO=Ne.experiment,GO=Ne.sessionReplay,KO=Ne.add,zO=Ne.extendSession,XO=Ne.flush,YO=Ne.getDeviceId,JO=Ne.getIdentity,QO=Ne.getOptOut,ZO=Ne.getSessionId,ex=Ne.getUserId,tx=Ne.groupIdentify,rx=Ne.identify,nx=Ne.logEvent,ix=Ne.remove,ox=Ne.reset,sx=Ne.revenue,ax=Ne.setDeviceId,ux=Ne.setGroup,lx=Ne.setIdentity,cx=Ne.setOptOut,fx=Ne.setSessionId,dx=Ne.setTransport,hx=Ne.setUserId,vx=Ne.track;const Qm=Object.freeze(Object.defineProperty({__proto__:null,Identify:Dn,Revenue:Hp,Types:QA,_enableRequestBodyCompressionExperimental:$O,_setDiagnosticsSampleRate:VO,add:KO,createInstance:Ym,experiment:WO,extendSession:zO,flush:XO,getDeviceId:YO,getIdentity:JO,getOptOut:QO,getSessionId:ZO,getUserId:ex,groupIdentify:tx,identify:rx,initAll:Jm,logEvent:nx,remove:ix,reset:ox,revenue:sx,sessionReplay:GO,setDeviceId:ax,setGroup:ux,setIdentity:lx,setOptOut:cx,setSessionId:fx,setTransport:dx,setUserId:hx,track:vx},Symbol.toStringTag,{value:"Module"})),px=tr(e=>{const r=to().public.amplitudeApiKey;return r&&Jm(r),{provide:{amplitude:Qm}}}),gx=[K_,Q_,Sw,Tw,Aw,Iw,kw,Rw,Ow,px],mx=(...e)=>e.find(t=>t!==void 0);function yx(e){const t=e.componentName||"NuxtLink";function r(o){return typeof o=="string"&&o.startsWith("#")}function n(o,s,a){const u=a??e.trailingSlash;if(!o||u!=="append"&&u!=="remove")return o;if(typeof o=="string")return xo(o,u);const c="path"in o&&o.path!==void 0?o.path:s(o).path;return{...o,name:void 0,path:xo(c,u)}}function i(o){const s=St(),a=to(),u=We(()=>!!o.target&&o.target!=="_self"),c=We(()=>{const b=o.to||o.href||"";return typeof b=="string"&&Hr(b,{acceptRelative:!0})}),l=sc("RouterLink"),f=l&&typeof l!="string"?l.useLink:void 0,d=We(()=>{if(o.external)return!0;const b=o.to||o.href||"";return typeof b=="object"?!1:b===""||c.value}),h=We(()=>{const b=o.to||o.href||"";return d.value?b:n(b,s.resolve,o.trailingSlash)}),v=d.value?void 0:f?.({...o,to:h}),p=We(()=>{const b=o.trailingSlash??e.trailingSlash;if(!h.value||c.value||r(h.value))return h.value;if(d.value){const E=typeof h.value=="object"&&"path"in h.value?uu(h.value):h.value,m=typeof E=="object"?s.resolve(E).href:E;return xo(m,b)}return typeof h.value=="object"?s.resolve(h.value)?.href??null:xo(gl(a.app.baseURL,h.value),b)});return{to:h,hasTarget:u,isAbsoluteUrl:c,isExternal:d,href:p,isActive:v?.isActive??We(()=>h.value===s.currentRoute.value.path),isExactActive:v?.isExactActive??We(()=>h.value===s.currentRoute.value.path),route:v?.route??We(()=>s.resolve(h.value)),async navigate(b){await up(p.value,{replace:o.replace,external:d.value||u.value})}}}return fn({name:t,props:{to:{type:[String,Object],default:void 0,required:!1},href:{type:[String,Object],default:void 0,required:!1},target:{type:String,default:void 0,required:!1},rel:{type:String,default:void 0,required:!1},noRel:{type:Boolean,default:void 0,required:!1},prefetch:{type:Boolean,default:void 0,required:!1},prefetchOn:{type:[String,Object],default:void 0,required:!1},noPrefetch:{type:Boolean,default:void 0,required:!1},activeClass:{type:String,default:void 0,required:!1},exactActiveClass:{type:String,default:void 0,required:!1},prefetchedClass:{type:String,default:void 0,required:!1},replace:{type:Boolean,default:void 0,required:!1},ariaCurrentValue:{type:String,default:void 0,required:!1},external:{type:Boolean,default:void 0,required:!1},custom:{type:Boolean,default:void 0,required:!1},trailingSlash:{type:String,default:void 0,required:!1}},useLink:i,setup(o,{slots:s}){const a=St(),{to:u,href:c,navigate:l,isExternal:f,hasTarget:d,isAbsoluteUrl:h}=i(o),v=Bn(!1),p=Jt(null),b=g=>{p.value=o.custom?g?.$el?.nextElementSibling:g?.$el};function E(g){return!v.value&&(typeof o.prefetchOn=="string"?o.prefetchOn===g:o.prefetchOn?.[g]??e.prefetchOn?.[g])&&(o.prefetch??e.prefetch)!==!1&&o.noPrefetch!==!0&&o.target!=="_blank"&&!Sx()}async function m(g=Ve()){if(v.value)return;v.value=!0;const y=typeof u.value=="string"?u.value:f.value?uu(u.value):a.resolve(u.value).fullPath,_=f.value?new URL(y,window.location.href).href:y;await Promise.all([g.hooks.callHook("link:prefetch",_).catch(()=>{}),!f.value&&!d.value&&Np(u.value,a).catch(()=>{})])}if(E("visibility")){const g=Ve();let y,_=null;Rs(()=>{const S=Ex();Ds(()=>{y=mu(()=>{p?.value?.tagName&&(_=S.observe(p.value,async()=>{_?.(),_=null,await m(g)}))})})}),Qn(()=>{y&&ww(y),_?.(),_=null})}return()=>{if(!f.value&&!d.value&&!r(u.value)){const _={ref:b,to:u.value,activeClass:o.activeClass||e.activeClass,exactActiveClass:o.exactActiveClass||e.exactActiveClass,replace:o.replace,ariaCurrentValue:o.ariaCurrentValue,custom:o.custom};return o.custom||(E("interaction")&&(_.onPointerenter=m.bind(null,void 0),_.onFocus=m.bind(null,void 0)),v.value&&(_.class=o.prefetchedClass||e.prefetchedClass),_.rel=o.rel||void 0),Et(sc("RouterLink"),_,s.default)}const g=o.target||null,y=mx(o.noRel?"":o.rel,e.externalRelAttribute,h.value||d.value?"noopener noreferrer":"")||null;return o.custom?s.default?s.default({href:c.value,navigate:l,prefetch:m,get route(){if(!c.value)return;const _=new URL(c.value,window.location.href);return{path:_.pathname,fullPath:_.pathname,get query(){return pl(_.search)},hash:_.hash,params:{},name:void 0,matched:[],redirectedFrom:void 0,meta:{},href:c.value}},rel:y,target:g,isExternal:f.value||d.value,isActive:!1,isExactActive:!1}):null:Et("a",{ref:p,href:c.value||null,rel:y,target:g,onClick:_=>{if(!(f.value||d.value))return _.preventDefault(),o.replace?a.replace(c.value):a.push(c.value)}},s.default?.())}}})}const bx=yx(HE);function xo(e,t){const r=t==="append"?Xv:Ki;return Hr(e)&&!e.startsWith("http")?e:r(e,!0)}function Ex(){const e=Ve();if(e._observer)return e._observer;let t=null;const r=new Map,n=(o,s)=>(t||=new IntersectionObserver(a=>{for(const u of a){const c=r.get(u.target);(u.isIntersecting||u.intersectionRatio>0)&&c&&c()}}),r.set(o,s),t.observe(o),()=>{r.delete(o),t?.unobserve(o),r.size===0&&(t?.disconnect(),t=null)});return e._observer={observe:n}}const _x=/2g/;function Sx(){const e=navigator.connection;return!!(e&&(e.saveData||_x.test(e.effectiveType)))}function wx(){return Qm}const Tx="$s";function Ax(...e){const t=typeof e[e.length-1]=="string"?e.pop():void 0;typeof e[0]!="string"&&e.unshift(t);const[r,n]=e;if(!r||typeof r!="string")throw new TypeError("[nuxt] [useState] key must be a string: "+r);if(n!==void 0&&typeof n!="function")throw new Error("[nuxt] [useState] init must be a function: "+n);const i=Tx+r,o=Ve(),s=Jh(o.payload.state,i);if(s.value===void 0&&n){const a=n();if(qe(a))return o.payload.state[i]=a,a;s.value=a}return s}const yi=new Map;function Ix(){const e=Ax("auth-user",()=>{{const o=localStorage.getItem("currentUser");if(o){const s=yi.get(o);if(s)return s}}return null});return{user:e,login:async(o,s)=>{try{const a=await $fetch("/api/auth/login",{method:"POST",body:{username:o,password:s}});if(a.success){let u=yi.get(o);return u||(u=a.user,yi.set(o,u)),e.value=u,localStorage.setItem("currentUser",o),!0}return!1}catch(a){return console.error("Login error:",a),!1}},logout:()=>{e.value=null,localStorage.removeItem("currentUser")},incrementBurritoConsiderations:()=>{e.value&&(e.value.burritoConsiderations++,yi.set(e.value.username,e.value),e.value={...e.value})},setUser:o=>{e.value=o,yi.set(o.username,o)}}}const Cx={class:"header"},kx={class:"header-container"},Rx={class:"user-section"},Px={key:0},Ox={key:1},xx=fn({__name:"AppHeader",setup(e){const t=wx(),r=Ix(),n=We(()=>r.user.value),i=async()=>{t?.track("user_logged_out"),t?.reset(),r.logout(),await up("/")};return(o,s)=>{const a=bx;return vt(),Xr("header",Cx,[Mn("div",kx,[Mn("nav",null,[Fe(a,{to:"/"},{default:Ai(()=>[...s[0]||(s[0]=[ki("Home",-1)])]),_:1}),ke(n)?(vt(),Xr(ht,{key:0},[Fe(a,{to:"/burrito"},{default:Ai(()=>[...s[1]||(s[1]=[ki("Burrito Consideration",-1)])]),_:1}),Fe(a,{to:"/profile"},{default:Ai(()=>[...s[2]||(s[2]=[ki("Profile",-1)])]),_:1})],64)):pc("",!0)]),Mn("div",Rx,[ke(n)?(vt(),Xr("span",Px,"Welcome, "+Oh(ke(n).username)+"!",1)):(vt(),Xr("span",Ox,"Not logged in")),ke(n)?(vt(),Xr("button",{key:2,onClick:i,class:"btn-logout"},"Logout")):pc("",!0)])])])}}}),Nx=Object.assign(xx,{__name:"AppHeader"}),Zm=(e="RouteProvider")=>fn({name:e,props:{route:{type:Object,required:!0},vnode:Object,vnodeRef:Object,renderKey:String,trackRootNodes:Boolean},setup(t){const r=t.renderKey,n=t.route,i={};for(const o in t.route)Object.defineProperty(i,o,{get:()=>r===t.renderKey?t.route[o]:n[o],enumerable:!0});return On(Ns,dr(i)),()=>t.vnode?Et(t.vnode,{ref:t.vnodeRef}):t.vnode}}),Lx=Zm(),bh=new WeakMap,Mx=fn({name:"NuxtPage",inheritAttrs:!1,props:{name:{type:String},transition:{type:[Boolean,Object],default:void 0},keepalive:{type:[Boolean,Object],default:void 0},route:{type:Object},pageKey:{type:[Function,String],default:null}},setup(e,{attrs:t,slots:r,expose:n}){const i=Ve(),o=Jt(),s=It(Ns,null);let a;n({pageRef:o});const u=It(ZE,null);let c;const l=i.deferHydration();let f=!1,d=0;if(i.isHydrating){const v=i.hooks.hookOnce("app:error",l);St().beforeEach(v)}e.pageKey&&xn(()=>e.pageKey,(v,p)=>{v!==p&&i.callHook("page:loading:start")});let h=!1;{const v=St().beforeResolve(()=>{h=!1});Qn(()=>{v()})}return()=>Et(Op,{name:e.name,route:e.route,...t},{default:v=>{const p=Ux(s,v.route,v.Component),b=s&&s.matched.length===v.route.matched.length;if(!v.Component){if(c&&!b)return c;l();return}if(c&&u&&!u.isCurrent(v.route))return c;if(p&&s&&(!u||u?.isCurrent(s)))return b?c:null;const E=pu(v,e.pageKey),m=Fx(s,v.route,v.Component);!i.isHydrating&&a===E&&!m&&qi(()=>{h||(h=!0,i.callHook("page:loading:end"))}),f&&a!==E&&d++,a=E;const g=!!(e.transition??v.route.meta.pageTransition??$c),y=g&&Dx([e.transition,v.route.meta.pageTransition,$c,{onAfterLeave(){delete i._runningTransition,i.callHook("page:transition:finish",v.Component)}}]),_=e.keepalive??v.route.meta.keepalive??FE;return c=fw(g&&y,uw(_,Et(Mv,{key:d,suspensible:!0,onPending:()=>{f=!0,g&&(i._runningTransition=!0),i.callHook("page:start",v.Component)},onResolve:async()=>{f=!1;try{await qi(),i._route.sync?.(),await i.callHook("page:finish",v.Component),delete i._runningTransition,!h&&!m&&(h=!0,await i.callHook("page:loading:end"))}finally{l()}}},{default:()=>{const S={key:E||void 0,vnode:r.default?Hx(r.default,v):v.Component,route:v.route,renderKey:E||void 0,trackRootNodes:g,vnodeRef:o};if(!_)return Et(Lx,S);const A=v.Component.type,I=A;let C=bh.get(I);return C||(C=Zm(A.name||A.__name),bh.set(I,C)),Et(C,S)}}))).default(),c}})}});function Dx(e){const t=[];for(const r of e)r&&t.push({...r,onAfterLeave:r.onAfterLeave?Sl(r.onAfterLeave):void 0});return sp(...t)}function Ux(e,t,r){if(!e)return!1;const n=t.matched.findIndex(i=>i.components?.default===r?.type);return!n||n===-1?!1:t.matched.slice(0,n).some((i,o)=>i.components?.default!==e.matched[o]?.components?.default)||r&&pu({route:t,Component:r})!==pu({route:e,Component:r})}function Fx(e,t,r){return e?t.matched.findIndex(i=>i.components?.default===r?.type){const r=e.__vccOpts||e;for(const[n,i]of t)r[n]=i;return r},jx={},qx={style:{"min-height":"100vh",display:"flex","flex-direction":"column",background:"#f5f5f5",width:"100%"}},Vx={style:{flex:"1"}};function $x(e,t){const r=Nx,n=Mx;return vt(),Xr("div",qx,[Fe(r),Mn("main",Vx,[Fe(n)])])}const Wx=Bx(jx,[["render",$x]]),Gx={__name:"nuxt-error-page",props:{error:Object},setup(e){const r=e.error,n=Number(r.statusCode||500),i=n===404,o=r.statusMessage??(i?"Page Not Found":"Internal Server Error"),s=r.message||r.toString(),a=void 0,l=i?oc(()=>Bt(()=>import("./CaxW3SQp.js"),__vite__mapDeps([0,1,2]),import.meta.url)):oc(()=>Bt(()=>import("./BaEVHuX6.js"),__vite__mapDeps([3,1,4]),import.meta.url));return(f,d)=>(vt(),Yr(ke(l),cy(jv({status:ke(n),statusText:ke(o),statusCode:ke(n),statusMessage:ke(o),description:ke(s),stack:ke(a)})),null,16))}},Kx={key:0},Eh={__name:"nuxt-root",setup(e){const t=()=>null,r=Ve(),n=r.deferHydration();if(r.isHydrating){const c=r.hooks.hookOnce("app:error",n);St().beforeEach(c)}const i=!1;On(Ns,e_()),r.hooks.callHookWith(c=>c.map(l=>l()),"vue:setup");const o=Ls(),s=!1,a=/bot\b|chrome-lighthouse|facebookexternalhit|google\b/i;vv((c,l,f)=>{if(r.hooks.callHook("vue:error",c,l,f).catch(d=>console.error("[nuxt] Error in `vue:error` hook",d)),a.test(navigator.userAgent))return r.hooks.callHook("app:error",c),console.error(`[nuxt] Not rendering error page for bot with user agent \`${navigator.userAgent}\`:`,c),!1;if(cp(c)&&(c.fatal||c.unhandled))return r.runWithContext(()=>Kr(c)),!1});const u=!1;return(c,l)=>(vt(),Yr(Mv,{onResolve:ke(n)},{default:Ai(()=>[ke(s)?(vt(),Xr("div",Kx)):ke(o)?(vt(),Yr(ke(Gx),{key:1,error:ke(o)},null,8,["error"])):ke(u)?(vt(),Yr(ke(t),{key:2,context:ke(u)},null,8,["context"])):ke(i)?(vt(),Yr(h0(ke(i)),{key:3})):(vt(),Yr(ke(Wx),{key:4}))]),_:1},8,["onResolve"]))}};let _h;{let e;_h=async function(){if(e)return e;const r=!!(window.__NUXT__?.serverRendered??document.getElementById("__NUXT_DATA__")?.dataset.ssr==="true"),n=r?xb(Eh):Ob(Eh),i=VE({vueApp:n});async function o(s){await i.callHook("app:error",s),i.payload.error||=on(s)}n.config.errorHandler=o,i.hook("app:suspense:resolve",()=>{n.config.errorHandler===o&&(n.config.errorHandler=void 0)}),!r&&Wc.id&&i.hook("app:suspense:resolve",()=>{document.getElementById(Wc.id)?.remove()});try{await GE(i,gx)}catch(s){o(s)}try{await i.hooks.callHook("app:created",n),await i.hooks.callHook("app:beforeMount",n),n.mount(BE),await i.hooks.callHook("app:mounted",n),await qi()}catch(s){o(s)}return n},e=_h().catch(t=>{throw console.error("Error while mounting app:",t),t})}export{Zx as A,x as B,O as C,le as D,gm as E,D as F,Dn as I,Bx as _,Mn as a,Fe as b,Xr as c,ki as d,bx as e,Ve as f,pp as g,ul as h,It as i,fn as j,Ix as k,ke as l,zx as m,Is as n,vt as o,pc as p,Yx as q,We as r,Fr as s,Oh as t,eN as u,Xx as v,Ai as w,Jt as x,up as y,wx as z}; - -``` - ---- - -## .output/public/_nuxt/BTkul5Q2.js - -```js -import{j as je,k as Ee,o as Z,c as R,l as b,t as B,a as C,m as oe,v as de,n as ce,p as K,q as $e,r as Ve,s as ue,x as le,I as Pe,y as Me,z as Le}from"./BpsV6fwO.js";var x;(function(r){r.assertEqual=a=>{};function e(a){}r.assertIs=e;function t(a){throw new Error}r.assertNever=t,r.arrayToEnum=a=>{const n={};for(const i of a)n[i]=i;return n},r.getValidEnumValues=a=>{const n=r.objectKeys(a).filter(o=>typeof a[a[o]]!="number"),i={};for(const o of n)i[o]=a[o];return r.objectValues(i)},r.objectValues=a=>r.objectKeys(a).map(function(n){return a[n]}),r.objectKeys=typeof Object.keys=="function"?a=>Object.keys(a):a=>{const n=[];for(const i in a)Object.prototype.hasOwnProperty.call(a,i)&&n.push(i);return n},r.find=(a,n)=>{for(const i of a)if(n(i))return i},r.isInteger=typeof Number.isInteger=="function"?a=>Number.isInteger(a):a=>typeof a=="number"&&Number.isFinite(a)&&Math.floor(a)===a;function s(a,n=" | "){return a.map(i=>typeof i=="string"?`'${i}'`:i).join(n)}r.joinValues=s,r.jsonStringifyReplacer=(a,n)=>typeof n=="bigint"?n.toString():n})(x||(x={}));var fe;(function(r){r.mergeShapes=(e,t)=>({...e,...t})})(fe||(fe={}));const u=x.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),I=r=>{switch(typeof r){case"undefined":return u.undefined;case"string":return u.string;case"number":return Number.isNaN(r)?u.nan:u.number;case"boolean":return u.boolean;case"function":return u.function;case"bigint":return u.bigint;case"symbol":return u.symbol;case"object":return Array.isArray(r)?u.array:r===null?u.null:r.then&&typeof r.then=="function"&&r.catch&&typeof r.catch=="function"?u.promise:typeof Map<"u"&&r instanceof Map?u.map:typeof Set<"u"&&r instanceof Set?u.set:typeof Date<"u"&&r instanceof Date?u.date:u.object;default:return u.unknown}},d=x.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]);class A extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=s=>{this.issues=[...this.issues,s]},this.addIssues=(s=[])=>{this.issues=[...this.issues,...s]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}format(e){const t=e||function(n){return n.message},s={_errors:[]},a=n=>{for(const i of n.issues)if(i.code==="invalid_union")i.unionErrors.map(a);else if(i.code==="invalid_return_type")a(i.returnTypeError);else if(i.code==="invalid_arguments")a(i.argumentsError);else if(i.path.length===0)s._errors.push(t(i));else{let o=s,f=0;for(;ft.message){const t={},s=[];for(const a of this.issues)if(a.path.length>0){const n=a.path[0];t[n]=t[n]||[],t[n].push(e(a))}else s.push(e(a));return{formErrors:s,fieldErrors:t}}get formErrors(){return this.flatten()}}A.create=r=>new A(r);const te=(r,e)=>{let t;switch(r.code){case d.invalid_type:r.received===u.undefined?t="Required":t=`Expected ${r.expected}, received ${r.received}`;break;case d.invalid_literal:t=`Invalid literal value, expected ${JSON.stringify(r.expected,x.jsonStringifyReplacer)}`;break;case d.unrecognized_keys:t=`Unrecognized key(s) in object: ${x.joinValues(r.keys,", ")}`;break;case d.invalid_union:t="Invalid input";break;case d.invalid_union_discriminator:t=`Invalid discriminator value. Expected ${x.joinValues(r.options)}`;break;case d.invalid_enum_value:t=`Invalid enum value. Expected ${x.joinValues(r.options)}, received '${r.received}'`;break;case d.invalid_arguments:t="Invalid function arguments";break;case d.invalid_return_type:t="Invalid function return type";break;case d.invalid_date:t="Invalid date";break;case d.invalid_string:typeof r.validation=="object"?"includes"in r.validation?(t=`Invalid input: must include "${r.validation.includes}"`,typeof r.validation.position=="number"&&(t=`${t} at one or more positions greater than or equal to ${r.validation.position}`)):"startsWith"in r.validation?t=`Invalid input: must start with "${r.validation.startsWith}"`:"endsWith"in r.validation?t=`Invalid input: must end with "${r.validation.endsWith}"`:x.assertNever(r.validation):r.validation!=="regex"?t=`Invalid ${r.validation}`:t="Invalid";break;case d.too_small:r.type==="array"?t=`Array must contain ${r.exact?"exactly":r.inclusive?"at least":"more than"} ${r.minimum} element(s)`:r.type==="string"?t=`String must contain ${r.exact?"exactly":r.inclusive?"at least":"over"} ${r.minimum} character(s)`:r.type==="number"?t=`Number must be ${r.exact?"exactly equal to ":r.inclusive?"greater than or equal to ":"greater than "}${r.minimum}`:r.type==="bigint"?t=`Number must be ${r.exact?"exactly equal to ":r.inclusive?"greater than or equal to ":"greater than "}${r.minimum}`:r.type==="date"?t=`Date must be ${r.exact?"exactly equal to ":r.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(r.minimum))}`:t="Invalid input";break;case d.too_big:r.type==="array"?t=`Array must contain ${r.exact?"exactly":r.inclusive?"at most":"less than"} ${r.maximum} element(s)`:r.type==="string"?t=`String must contain ${r.exact?"exactly":r.inclusive?"at most":"under"} ${r.maximum} character(s)`:r.type==="number"?t=`Number must be ${r.exact?"exactly":r.inclusive?"less than or equal to":"less than"} ${r.maximum}`:r.type==="bigint"?t=`BigInt must be ${r.exact?"exactly":r.inclusive?"less than or equal to":"less than"} ${r.maximum}`:r.type==="date"?t=`Date must be ${r.exact?"exactly":r.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(r.maximum))}`:t="Invalid input";break;case d.custom:t="Invalid input";break;case d.invalid_intersection_types:t="Intersection results could not be merged";break;case d.not_multiple_of:t=`Number must be a multiple of ${r.multipleOf}`;break;case d.not_finite:t="Number must be finite";break;default:t=e.defaultError,x.assertNever(r)}return{message:t}};let ze=te;function De(){return ze}const Ue=r=>{const{data:e,path:t,errorMaps:s,issueData:a}=r,n=[...t,...a.path||[]],i={...a,path:n};if(a.message!==void 0)return{...a,path:n,message:a.message};let o="";const f=s.filter(h=>!!h).slice().reverse();for(const h of f)o=h(i,{data:e,defaultError:o}).message;return{...a,path:n,message:o}};function c(r,e){const t=De(),s=Ue({issueData:e,data:r.data,path:r.path,errorMaps:[r.common.contextualErrorMap,r.schemaErrorMap,t,t===te?void 0:te].filter(a=>!!a)});r.common.issues.push(s)}class S{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(e,t){const s=[];for(const a of t){if(a.status==="aborted")return m;a.status==="dirty"&&e.dirty(),s.push(a.value)}return{status:e.value,value:s}}static async mergeObjectAsync(e,t){const s=[];for(const a of t){const n=await a.key,i=await a.value;s.push({key:n,value:i})}return S.mergeObjectSync(e,s)}static mergeObjectSync(e,t){const s={};for(const a of t){const{key:n,value:i}=a;if(n.status==="aborted"||i.status==="aborted")return m;n.status==="dirty"&&e.dirty(),i.status==="dirty"&&e.dirty(),n.value!=="__proto__"&&(typeof i.value<"u"||a.alwaysSet)&&(s[n.value]=i.value)}return{status:e.value,value:s}}}const m=Object.freeze({status:"aborted"}),F=r=>({status:"dirty",value:r}),O=r=>({status:"valid",value:r}),he=r=>r.status==="aborted",me=r=>r.status==="dirty",L=r=>r.status==="valid",Y=r=>typeof Promise<"u"&&r instanceof Promise;var l;(function(r){r.errToObj=e=>typeof e=="string"?{message:e}:e||{},r.toString=e=>typeof e=="string"?e:e?.message})(l||(l={}));class ${constructor(e,t,s,a){this._cachedPath=[],this.parent=e,this.data=t,this._path=s,this._key=a}get path(){return this._cachedPath.length||(Array.isArray(this._key)?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}}const pe=(r,e)=>{if(L(e))return{success:!0,data:e.value};if(!r.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;const t=new A(r.common.issues);return this._error=t,this._error}}};function g(r){if(!r)return{};const{errorMap:e,invalid_type_error:t,required_error:s,description:a}=r;if(e&&(t||s))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return e?{errorMap:e,description:a}:{errorMap:(i,o)=>{const{message:f}=r;return i.code==="invalid_enum_value"?{message:f??o.defaultError}:typeof o.data>"u"?{message:f??s??o.defaultError}:i.code!=="invalid_type"?{message:o.defaultError}:{message:f??t??o.defaultError}},description:a}}class _{get description(){return this._def.description}_getType(e){return I(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:I(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new S,ctx:{common:e.parent.common,data:e.data,parsedType:I(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(Y(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const s=this.safeParse(e,t);if(s.success)return s.data;throw s.error}safeParse(e,t){const s={common:{issues:[],async:t?.async??!1,contextualErrorMap:t?.errorMap},path:t?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:I(e)},a=this._parseSync({data:e,path:s.path,parent:s});return pe(s,a)}"~validate"(e){const t={common:{issues:[],async:!!this["~standard"].async},path:[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:I(e)};if(!this["~standard"].async)try{const s=this._parseSync({data:e,path:[],parent:t});return L(s)?{value:s.value}:{issues:t.common.issues}}catch(s){s?.message?.toLowerCase()?.includes("encountered")&&(this["~standard"].async=!0),t.common={issues:[],async:!0}}return this._parseAsync({data:e,path:[],parent:t}).then(s=>L(s)?{value:s.value}:{issues:t.common.issues})}async parseAsync(e,t){const s=await this.safeParseAsync(e,t);if(s.success)return s.data;throw s.error}async safeParseAsync(e,t){const s={common:{issues:[],contextualErrorMap:t?.errorMap,async:!0},path:t?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:I(e)},a=this._parse({data:e,path:s.path,parent:s}),n=await(Y(a)?a:Promise.resolve(a));return pe(s,n)}refine(e,t){const s=a=>typeof t=="string"||typeof t>"u"?{message:t}:typeof t=="function"?t(a):t;return this._refinement((a,n)=>{const i=e(a),o=()=>n.addIssue({code:d.custom,...s(a)});return typeof Promise<"u"&&i instanceof Promise?i.then(f=>f?!0:(o(),!1)):i?!0:(o(),!1)})}refinement(e,t){return this._refinement((s,a)=>e(s)?!0:(a.addIssue(typeof t=="function"?t(s,a):t),!1))}_refinement(e){return new D({schema:this,typeName:p.ZodEffects,effect:{type:"refinement",refinement:e}})}superRefine(e){return this._refinement(e)}constructor(e){this.spa=this.safeParseAsync,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this),this["~standard"]={version:1,vendor:"zod",validate:t=>this["~validate"](t)}}optional(){return E.create(this,this._def)}nullable(){return U.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return N.create(this)}promise(){return X.create(this,this._def)}or(e){return G.create([this,e],this._def)}and(e){return Q.create(this,e,this._def)}transform(e){return new D({...g(this._def),schema:this,typeName:p.ZodEffects,effect:{type:"transform",transform:e}})}default(e){const t=typeof e=="function"?e:()=>e;return new re({...g(this._def),innerType:this,defaultValue:t,typeName:p.ZodDefault})}brand(){return new lt({typeName:p.ZodBranded,type:this,...g(this._def)})}catch(e){const t=typeof e=="function"?e:()=>e;return new ae({...g(this._def),innerType:this,catchValue:t,typeName:p.ZodCatch})}describe(e){const t=this.constructor;return new t({...this._def,description:e})}pipe(e){return ie.create(this,e)}readonly(){return ne.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}const Be=/^c[^\s-]{8,}$/i,Fe=/^[0-9a-z]+$/,We=/^[0-9A-HJKMNP-TV-Z]{26}$/i,qe=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,Je=/^[a-z0-9_-]{21}$/i,Ye=/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/,He=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,Ge=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,Qe="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";let ee;const Xe=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,Ke=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,et=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,tt=/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,st=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,rt=/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,Ze="((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))",at=new RegExp(`^${Ze}$`);function Re(r){let e="[0-5]\\d";r.precision?e=`${e}\\.\\d{${r.precision}}`:r.precision==null&&(e=`${e}(\\.\\d+)?`);const t=r.precision?"+":"?";return`([01]\\d|2[0-3]):[0-5]\\d(:${e})${t}`}function nt(r){return new RegExp(`^${Re(r)}$`)}function it(r){let e=`${Ze}T${Re(r)}`;const t=[];return t.push(r.local?"Z?":"Z"),r.offset&&t.push("([+-]\\d{2}:?\\d{2})"),e=`${e}(${t.join("|")})`,new RegExp(`^${e}$`)}function ot(r,e){return!!((e==="v4"||!e)&&Xe.test(r)||(e==="v6"||!e)&&et.test(r))}function dt(r,e){if(!Ye.test(r))return!1;try{const[t]=r.split(".");if(!t)return!1;const s=t.replace(/-/g,"+").replace(/_/g,"/").padEnd(t.length+(4-t.length%4)%4,"="),a=JSON.parse(atob(s));return!(typeof a!="object"||a===null||"typ"in a&&a?.typ!=="JWT"||!a.alg||e&&a.alg!==e)}catch{return!1}}function ct(r,e){return!!((e==="v4"||!e)&&Ke.test(r)||(e==="v6"||!e)&&tt.test(r))}class j extends _{_parse(e){if(this._def.coerce&&(e.data=String(e.data)),this._getType(e)!==u.string){const n=this._getOrReturnCtx(e);return c(n,{code:d.invalid_type,expected:u.string,received:n.parsedType}),m}const s=new S;let a;for(const n of this._def.checks)if(n.kind==="min")e.data.lengthn.value&&(a=this._getOrReturnCtx(e,a),c(a,{code:d.too_big,maximum:n.value,type:"string",inclusive:!0,exact:!1,message:n.message}),s.dirty());else if(n.kind==="length"){const i=e.data.length>n.value,o=e.data.lengthe.test(a),{validation:t,code:d.invalid_string,...l.errToObj(s)})}_addCheck(e){return new j({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...l.errToObj(e)})}url(e){return this._addCheck({kind:"url",...l.errToObj(e)})}emoji(e){return this._addCheck({kind:"emoji",...l.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...l.errToObj(e)})}nanoid(e){return this._addCheck({kind:"nanoid",...l.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...l.errToObj(e)})}cuid2(e){return this._addCheck({kind:"cuid2",...l.errToObj(e)})}ulid(e){return this._addCheck({kind:"ulid",...l.errToObj(e)})}base64(e){return this._addCheck({kind:"base64",...l.errToObj(e)})}base64url(e){return this._addCheck({kind:"base64url",...l.errToObj(e)})}jwt(e){return this._addCheck({kind:"jwt",...l.errToObj(e)})}ip(e){return this._addCheck({kind:"ip",...l.errToObj(e)})}cidr(e){return this._addCheck({kind:"cidr",...l.errToObj(e)})}datetime(e){return typeof e=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:e}):this._addCheck({kind:"datetime",precision:typeof e?.precision>"u"?null:e?.precision,offset:e?.offset??!1,local:e?.local??!1,...l.errToObj(e?.message)})}date(e){return this._addCheck({kind:"date",message:e})}time(e){return typeof e=="string"?this._addCheck({kind:"time",precision:null,message:e}):this._addCheck({kind:"time",precision:typeof e?.precision>"u"?null:e?.precision,...l.errToObj(e?.message)})}duration(e){return this._addCheck({kind:"duration",...l.errToObj(e)})}regex(e,t){return this._addCheck({kind:"regex",regex:e,...l.errToObj(t)})}includes(e,t){return this._addCheck({kind:"includes",value:e,position:t?.position,...l.errToObj(t?.message)})}startsWith(e,t){return this._addCheck({kind:"startsWith",value:e,...l.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:"endsWith",value:e,...l.errToObj(t)})}min(e,t){return this._addCheck({kind:"min",value:e,...l.errToObj(t)})}max(e,t){return this._addCheck({kind:"max",value:e,...l.errToObj(t)})}length(e,t){return this._addCheck({kind:"length",value:e,...l.errToObj(t)})}nonempty(e){return this.min(1,l.errToObj(e))}trim(){return new j({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}toLowerCase(){return new j({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]})}toUpperCase(){return new j({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}get isDatetime(){return!!this._def.checks.find(e=>e.kind==="datetime")}get isDate(){return!!this._def.checks.find(e=>e.kind==="date")}get isTime(){return!!this._def.checks.find(e=>e.kind==="time")}get isDuration(){return!!this._def.checks.find(e=>e.kind==="duration")}get isEmail(){return!!this._def.checks.find(e=>e.kind==="email")}get isURL(){return!!this._def.checks.find(e=>e.kind==="url")}get isEmoji(){return!!this._def.checks.find(e=>e.kind==="emoji")}get isUUID(){return!!this._def.checks.find(e=>e.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(e=>e.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(e=>e.kind==="cuid")}get isCUID2(){return!!this._def.checks.find(e=>e.kind==="cuid2")}get isULID(){return!!this._def.checks.find(e=>e.kind==="ulid")}get isIP(){return!!this._def.checks.find(e=>e.kind==="ip")}get isCIDR(){return!!this._def.checks.find(e=>e.kind==="cidr")}get isBase64(){return!!this._def.checks.find(e=>e.kind==="base64")}get isBase64url(){return!!this._def.checks.find(e=>e.kind==="base64url")}get minLength(){let e=null;for(const t of this._def.checks)t.kind==="min"&&(e===null||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)t.kind==="max"&&(e===null||t.valuenew j({checks:[],typeName:p.ZodString,coerce:r?.coerce??!1,...g(r)});function ut(r,e){const t=(r.toString().split(".")[1]||"").length,s=(e.toString().split(".")[1]||"").length,a=t>s?t:s,n=Number.parseInt(r.toFixed(a).replace(".","")),i=Number.parseInt(e.toFixed(a).replace(".",""));return n%i/10**a}class W extends _{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._def.coerce&&(e.data=Number(e.data)),this._getType(e)!==u.number){const n=this._getOrReturnCtx(e);return c(n,{code:d.invalid_type,expected:u.number,received:n.parsedType}),m}let s;const a=new S;for(const n of this._def.checks)n.kind==="int"?x.isInteger(e.data)||(s=this._getOrReturnCtx(e,s),c(s,{code:d.invalid_type,expected:"integer",received:"float",message:n.message}),a.dirty()):n.kind==="min"?(n.inclusive?e.datan.value:e.data>=n.value)&&(s=this._getOrReturnCtx(e,s),c(s,{code:d.too_big,maximum:n.value,type:"number",inclusive:n.inclusive,exact:!1,message:n.message}),a.dirty()):n.kind==="multipleOf"?ut(e.data,n.value)!==0&&(s=this._getOrReturnCtx(e,s),c(s,{code:d.not_multiple_of,multipleOf:n.value,message:n.message}),a.dirty()):n.kind==="finite"?Number.isFinite(e.data)||(s=this._getOrReturnCtx(e,s),c(s,{code:d.not_finite,message:n.message}),a.dirty()):x.assertNever(n);return{status:a.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,l.toString(t))}gt(e,t){return this.setLimit("min",e,!1,l.toString(t))}lte(e,t){return this.setLimit("max",e,!0,l.toString(t))}lt(e,t){return this.setLimit("max",e,!1,l.toString(t))}setLimit(e,t,s,a){return new W({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:s,message:l.toString(a)}]})}_addCheck(e){return new W({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:l.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:l.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:l.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:l.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:l.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:l.toString(t)})}finite(e){return this._addCheck({kind:"finite",message:l.toString(e)})}safe(e){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:l.toString(e)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:l.toString(e)})}get minValue(){let e=null;for(const t of this._def.checks)t.kind==="min"&&(e===null||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)t.kind==="max"&&(e===null||t.valuee.kind==="int"||e.kind==="multipleOf"&&x.isInteger(e.value))}get isFinite(){let e=null,t=null;for(const s of this._def.checks){if(s.kind==="finite"||s.kind==="int"||s.kind==="multipleOf")return!0;s.kind==="min"?(t===null||s.value>t)&&(t=s.value):s.kind==="max"&&(e===null||s.valuenew W({checks:[],typeName:p.ZodNumber,coerce:r?.coerce||!1,...g(r)});class q extends _{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(e){if(this._def.coerce)try{e.data=BigInt(e.data)}catch{return this._getInvalidInput(e)}if(this._getType(e)!==u.bigint)return this._getInvalidInput(e);let s;const a=new S;for(const n of this._def.checks)n.kind==="min"?(n.inclusive?e.datan.value:e.data>=n.value)&&(s=this._getOrReturnCtx(e,s),c(s,{code:d.too_big,type:"bigint",maximum:n.value,inclusive:n.inclusive,message:n.message}),a.dirty()):n.kind==="multipleOf"?e.data%n.value!==BigInt(0)&&(s=this._getOrReturnCtx(e,s),c(s,{code:d.not_multiple_of,multipleOf:n.value,message:n.message}),a.dirty()):x.assertNever(n);return{status:a.value,value:e.data}}_getInvalidInput(e){const t=this._getOrReturnCtx(e);return c(t,{code:d.invalid_type,expected:u.bigint,received:t.parsedType}),m}gte(e,t){return this.setLimit("min",e,!0,l.toString(t))}gt(e,t){return this.setLimit("min",e,!1,l.toString(t))}lte(e,t){return this.setLimit("max",e,!0,l.toString(t))}lt(e,t){return this.setLimit("max",e,!1,l.toString(t))}setLimit(e,t,s,a){return new q({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:s,message:l.toString(a)}]})}_addCheck(e){return new q({...this._def,checks:[...this._def.checks,e]})}positive(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:l.toString(e)})}negative(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:l.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:l.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:l.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:l.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)t.kind==="min"&&(e===null||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)t.kind==="max"&&(e===null||t.valuenew q({checks:[],typeName:p.ZodBigInt,coerce:r?.coerce??!1,...g(r)});class ge extends _{_parse(e){if(this._def.coerce&&(e.data=!!e.data),this._getType(e)!==u.boolean){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.boolean,received:s.parsedType}),m}return O(e.data)}}ge.create=r=>new ge({typeName:p.ZodBoolean,coerce:r?.coerce||!1,...g(r)});class H extends _{_parse(e){if(this._def.coerce&&(e.data=new Date(e.data)),this._getType(e)!==u.date){const n=this._getOrReturnCtx(e);return c(n,{code:d.invalid_type,expected:u.date,received:n.parsedType}),m}if(Number.isNaN(e.data.getTime())){const n=this._getOrReturnCtx(e);return c(n,{code:d.invalid_date}),m}const s=new S;let a;for(const n of this._def.checks)n.kind==="min"?e.data.getTime()n.value&&(a=this._getOrReturnCtx(e,a),c(a,{code:d.too_big,message:n.message,inclusive:!0,exact:!1,maximum:n.value,type:"date"}),s.dirty()):x.assertNever(n);return{status:s.value,value:new Date(e.data.getTime())}}_addCheck(e){return new H({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:"min",value:e.getTime(),message:l.toString(t)})}max(e,t){return this._addCheck({kind:"max",value:e.getTime(),message:l.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)t.kind==="min"&&(e===null||t.value>e)&&(e=t.value);return e!=null?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)t.kind==="max"&&(e===null||t.valuenew H({checks:[],coerce:r?.coerce||!1,typeName:p.ZodDate,...g(r)});class ye extends _{_parse(e){if(this._getType(e)!==u.symbol){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.symbol,received:s.parsedType}),m}return O(e.data)}}ye.create=r=>new ye({typeName:p.ZodSymbol,...g(r)});class _e extends _{_parse(e){if(this._getType(e)!==u.undefined){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.undefined,received:s.parsedType}),m}return O(e.data)}}_e.create=r=>new _e({typeName:p.ZodUndefined,...g(r)});class ve extends _{_parse(e){if(this._getType(e)!==u.null){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.null,received:s.parsedType}),m}return O(e.data)}}ve.create=r=>new ve({typeName:p.ZodNull,...g(r)});class xe extends _{constructor(){super(...arguments),this._any=!0}_parse(e){return O(e.data)}}xe.create=r=>new xe({typeName:p.ZodAny,...g(r)});class ke extends _{constructor(){super(...arguments),this._unknown=!0}_parse(e){return O(e.data)}}ke.create=r=>new ke({typeName:p.ZodUnknown,...g(r)});class V extends _{_parse(e){const t=this._getOrReturnCtx(e);return c(t,{code:d.invalid_type,expected:u.never,received:t.parsedType}),m}}V.create=r=>new V({typeName:p.ZodNever,...g(r)});class be extends _{_parse(e){if(this._getType(e)!==u.undefined){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.void,received:s.parsedType}),m}return O(e.data)}}be.create=r=>new be({typeName:p.ZodVoid,...g(r)});class N extends _{_parse(e){const{ctx:t,status:s}=this._processInputParams(e),a=this._def;if(t.parsedType!==u.array)return c(t,{code:d.invalid_type,expected:u.array,received:t.parsedType}),m;if(a.exactLength!==null){const i=t.data.length>a.exactLength.value,o=t.data.lengtha.maxLength.value&&(c(t,{code:d.too_big,maximum:a.maxLength.value,type:"array",inclusive:!0,exact:!1,message:a.maxLength.message}),s.dirty()),t.common.async)return Promise.all([...t.data].map((i,o)=>a.type._parseAsync(new $(t,i,t.path,o)))).then(i=>S.mergeArray(s,i));const n=[...t.data].map((i,o)=>a.type._parseSync(new $(t,i,t.path,o)));return S.mergeArray(s,n)}get element(){return this._def.type}min(e,t){return new N({...this._def,minLength:{value:e,message:l.toString(t)}})}max(e,t){return new N({...this._def,maxLength:{value:e,message:l.toString(t)}})}length(e,t){return new N({...this._def,exactLength:{value:e,message:l.toString(t)}})}nonempty(e){return this.min(1,e)}}N.create=(r,e)=>new N({type:r,minLength:null,maxLength:null,exactLength:null,typeName:p.ZodArray,...g(e)});function M(r){if(r instanceof k){const e={};for(const t in r.shape){const s=r.shape[t];e[t]=E.create(M(s))}return new k({...r._def,shape:()=>e})}else return r instanceof N?new N({...r._def,type:M(r.element)}):r instanceof E?E.create(M(r.unwrap())):r instanceof U?U.create(M(r.unwrap())):r instanceof P?P.create(r.items.map(e=>M(e))):r}class k extends _{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;const e=this._def.shape(),t=x.objectKeys(e);return this._cached={shape:e,keys:t},this._cached}_parse(e){if(this._getType(e)!==u.object){const h=this._getOrReturnCtx(e);return c(h,{code:d.invalid_type,expected:u.object,received:h.parsedType}),m}const{status:s,ctx:a}=this._processInputParams(e),{shape:n,keys:i}=this._getCached(),o=[];if(!(this._def.catchall instanceof V&&this._def.unknownKeys==="strip"))for(const h in a.data)i.includes(h)||o.push(h);const f=[];for(const h of i){const y=n[h],w=a.data[h];f.push({key:{status:"valid",value:h},value:y._parse(new $(a,w,a.path,h)),alwaysSet:h in a.data})}if(this._def.catchall instanceof V){const h=this._def.unknownKeys;if(h==="passthrough")for(const y of o)f.push({key:{status:"valid",value:y},value:{status:"valid",value:a.data[y]}});else if(h==="strict")o.length>0&&(c(a,{code:d.unrecognized_keys,keys:o}),s.dirty());else if(h!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const h=this._def.catchall;for(const y of o){const w=a.data[y];f.push({key:{status:"valid",value:y},value:h._parse(new $(a,w,a.path,y)),alwaysSet:y in a.data})}}return a.common.async?Promise.resolve().then(async()=>{const h=[];for(const y of f){const w=await y.key,v=await y.value;h.push({key:w,value:v,alwaysSet:y.alwaysSet})}return h}).then(h=>S.mergeObjectSync(s,h)):S.mergeObjectSync(s,f)}get shape(){return this._def.shape()}strict(e){return l.errToObj,new k({...this._def,unknownKeys:"strict",...e!==void 0?{errorMap:(t,s)=>{const a=this._def.errorMap?.(t,s).message??s.defaultError;return t.code==="unrecognized_keys"?{message:l.errToObj(e).message??a}:{message:a}}}:{}})}strip(){return new k({...this._def,unknownKeys:"strip"})}passthrough(){return new k({...this._def,unknownKeys:"passthrough"})}extend(e){return new k({...this._def,shape:()=>({...this._def.shape(),...e})})}merge(e){return new k({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>({...this._def.shape(),...e._def.shape()}),typeName:p.ZodObject})}setKey(e,t){return this.augment({[e]:t})}catchall(e){return new k({...this._def,catchall:e})}pick(e){const t={};for(const s of x.objectKeys(e))e[s]&&this.shape[s]&&(t[s]=this.shape[s]);return new k({...this._def,shape:()=>t})}omit(e){const t={};for(const s of x.objectKeys(this.shape))e[s]||(t[s]=this.shape[s]);return new k({...this._def,shape:()=>t})}deepPartial(){return M(this)}partial(e){const t={};for(const s of x.objectKeys(this.shape)){const a=this.shape[s];e&&!e[s]?t[s]=a:t[s]=a.optional()}return new k({...this._def,shape:()=>t})}required(e){const t={};for(const s of x.objectKeys(this.shape))if(e&&!e[s])t[s]=this.shape[s];else{let n=this.shape[s];for(;n instanceof E;)n=n._def.innerType;t[s]=n}return new k({...this._def,shape:()=>t})}keyof(){return Ie(x.objectKeys(this.shape))}}k.create=(r,e)=>new k({shape:()=>r,unknownKeys:"strip",catchall:V.create(),typeName:p.ZodObject,...g(e)});k.strictCreate=(r,e)=>new k({shape:()=>r,unknownKeys:"strict",catchall:V.create(),typeName:p.ZodObject,...g(e)});k.lazycreate=(r,e)=>new k({shape:r,unknownKeys:"strip",catchall:V.create(),typeName:p.ZodObject,...g(e)});class G extends _{_parse(e){const{ctx:t}=this._processInputParams(e),s=this._def.options;function a(n){for(const o of n)if(o.result.status==="valid")return o.result;for(const o of n)if(o.result.status==="dirty")return t.common.issues.push(...o.ctx.common.issues),o.result;const i=n.map(o=>new A(o.ctx.common.issues));return c(t,{code:d.invalid_union,unionErrors:i}),m}if(t.common.async)return Promise.all(s.map(async n=>{const i={...t,common:{...t.common,issues:[]},parent:null};return{result:await n._parseAsync({data:t.data,path:t.path,parent:i}),ctx:i}})).then(a);{let n;const i=[];for(const f of s){const h={...t,common:{...t.common,issues:[]},parent:null},y=f._parseSync({data:t.data,path:t.path,parent:h});if(y.status==="valid")return y;y.status==="dirty"&&!n&&(n={result:y,ctx:h}),h.common.issues.length&&i.push(h.common.issues)}if(n)return t.common.issues.push(...n.ctx.common.issues),n.result;const o=i.map(f=>new A(f));return c(t,{code:d.invalid_union,unionErrors:o}),m}}get options(){return this._def.options}}G.create=(r,e)=>new G({options:r,typeName:p.ZodUnion,...g(e)});function se(r,e){const t=I(r),s=I(e);if(r===e)return{valid:!0,data:r};if(t===u.object&&s===u.object){const a=x.objectKeys(e),n=x.objectKeys(r).filter(o=>a.indexOf(o)!==-1),i={...r,...e};for(const o of n){const f=se(r[o],e[o]);if(!f.valid)return{valid:!1};i[o]=f.data}return{valid:!0,data:i}}else if(t===u.array&&s===u.array){if(r.length!==e.length)return{valid:!1};const a=[];for(let n=0;n{if(he(n)||he(i))return m;const o=se(n.value,i.value);return o.valid?((me(n)||me(i))&&t.dirty(),{status:t.value,value:o.data}):(c(s,{code:d.invalid_intersection_types}),m)};return s.common.async?Promise.all([this._def.left._parseAsync({data:s.data,path:s.path,parent:s}),this._def.right._parseAsync({data:s.data,path:s.path,parent:s})]).then(([n,i])=>a(n,i)):a(this._def.left._parseSync({data:s.data,path:s.path,parent:s}),this._def.right._parseSync({data:s.data,path:s.path,parent:s}))}}Q.create=(r,e,t)=>new Q({left:r,right:e,typeName:p.ZodIntersection,...g(t)});class P extends _{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==u.array)return c(s,{code:d.invalid_type,expected:u.array,received:s.parsedType}),m;if(s.data.lengththis._def.items.length&&(c(s,{code:d.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),t.dirty());const n=[...s.data].map((i,o)=>{const f=this._def.items[o]||this._def.rest;return f?f._parse(new $(s,i,s.path,o)):null}).filter(i=>!!i);return s.common.async?Promise.all(n).then(i=>S.mergeArray(t,i)):S.mergeArray(t,n)}get items(){return this._def.items}rest(e){return new P({...this._def,rest:e})}}P.create=(r,e)=>{if(!Array.isArray(r))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new P({items:r,typeName:p.ZodTuple,rest:null,...g(e)})};class we extends _{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==u.map)return c(s,{code:d.invalid_type,expected:u.map,received:s.parsedType}),m;const a=this._def.keyType,n=this._def.valueType,i=[...s.data.entries()].map(([o,f],h)=>({key:a._parse(new $(s,o,s.path,[h,"key"])),value:n._parse(new $(s,f,s.path,[h,"value"]))}));if(s.common.async){const o=new Map;return Promise.resolve().then(async()=>{for(const f of i){const h=await f.key,y=await f.value;if(h.status==="aborted"||y.status==="aborted")return m;(h.status==="dirty"||y.status==="dirty")&&t.dirty(),o.set(h.value,y.value)}return{status:t.value,value:o}})}else{const o=new Map;for(const f of i){const h=f.key,y=f.value;if(h.status==="aborted"||y.status==="aborted")return m;(h.status==="dirty"||y.status==="dirty")&&t.dirty(),o.set(h.value,y.value)}return{status:t.value,value:o}}}}we.create=(r,e,t)=>new we({valueType:e,keyType:r,typeName:p.ZodMap,...g(t)});class J extends _{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==u.set)return c(s,{code:d.invalid_type,expected:u.set,received:s.parsedType}),m;const a=this._def;a.minSize!==null&&s.data.sizea.maxSize.value&&(c(s,{code:d.too_big,maximum:a.maxSize.value,type:"set",inclusive:!0,exact:!1,message:a.maxSize.message}),t.dirty());const n=this._def.valueType;function i(f){const h=new Set;for(const y of f){if(y.status==="aborted")return m;y.status==="dirty"&&t.dirty(),h.add(y.value)}return{status:t.value,value:h}}const o=[...s.data.values()].map((f,h)=>n._parse(new $(s,f,s.path,h)));return s.common.async?Promise.all(o).then(f=>i(f)):i(o)}min(e,t){return new J({...this._def,minSize:{value:e,message:l.toString(t)}})}max(e,t){return new J({...this._def,maxSize:{value:e,message:l.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}J.create=(r,e)=>new J({valueType:r,minSize:null,maxSize:null,typeName:p.ZodSet,...g(e)});class Ce extends _{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}Ce.create=(r,e)=>new Ce({getter:r,typeName:p.ZodLazy,...g(e)});class Te extends _{_parse(e){if(e.data!==this._def.value){const t=this._getOrReturnCtx(e);return c(t,{received:t.data,code:d.invalid_literal,expected:this._def.value}),m}return{status:"valid",value:e.data}}get value(){return this._def.value}}Te.create=(r,e)=>new Te({value:r,typeName:p.ZodLiteral,...g(e)});function Ie(r,e){return new z({values:r,typeName:p.ZodEnum,...g(e)})}class z extends _{_parse(e){if(typeof e.data!="string"){const t=this._getOrReturnCtx(e),s=this._def.values;return c(t,{expected:x.joinValues(s),received:t.parsedType,code:d.invalid_type}),m}if(this._cache||(this._cache=new Set(this._def.values)),!this._cache.has(e.data)){const t=this._getOrReturnCtx(e),s=this._def.values;return c(t,{received:t.data,code:d.invalid_enum_value,options:s}),m}return O(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}extract(e,t=this._def){return z.create(e,{...this._def,...t})}exclude(e,t=this._def){return z.create(this.options.filter(s=>!e.includes(s)),{...this._def,...t})}}z.create=Ie;class Se extends _{_parse(e){const t=x.getValidEnumValues(this._def.values),s=this._getOrReturnCtx(e);if(s.parsedType!==u.string&&s.parsedType!==u.number){const a=x.objectValues(t);return c(s,{expected:x.joinValues(a),received:s.parsedType,code:d.invalid_type}),m}if(this._cache||(this._cache=new Set(x.getValidEnumValues(this._def.values))),!this._cache.has(e.data)){const a=x.objectValues(t);return c(s,{received:s.data,code:d.invalid_enum_value,options:a}),m}return O(e.data)}get enum(){return this._def.values}}Se.create=(r,e)=>new Se({values:r,typeName:p.ZodNativeEnum,...g(e)});class X extends _{unwrap(){return this._def.type}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==u.promise&&t.common.async===!1)return c(t,{code:d.invalid_type,expected:u.promise,received:t.parsedType}),m;const s=t.parsedType===u.promise?t.data:Promise.resolve(t.data);return O(s.then(a=>this._def.type.parseAsync(a,{path:t.path,errorMap:t.common.contextualErrorMap})))}}X.create=(r,e)=>new X({type:r,typeName:p.ZodPromise,...g(e)});class D extends _{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===p.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(e){const{status:t,ctx:s}=this._processInputParams(e),a=this._def.effect||null,n={addIssue:i=>{c(s,i),i.fatal?t.abort():t.dirty()},get path(){return s.path}};if(n.addIssue=n.addIssue.bind(n),a.type==="preprocess"){const i=a.transform(s.data,n);if(s.common.async)return Promise.resolve(i).then(async o=>{if(t.value==="aborted")return m;const f=await this._def.schema._parseAsync({data:o,path:s.path,parent:s});return f.status==="aborted"?m:f.status==="dirty"||t.value==="dirty"?F(f.value):f});{if(t.value==="aborted")return m;const o=this._def.schema._parseSync({data:i,path:s.path,parent:s});return o.status==="aborted"?m:o.status==="dirty"||t.value==="dirty"?F(o.value):o}}if(a.type==="refinement"){const i=o=>{const f=a.refinement(o,n);if(s.common.async)return Promise.resolve(f);if(f instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return o};if(s.common.async===!1){const o=this._def.schema._parseSync({data:s.data,path:s.path,parent:s});return o.status==="aborted"?m:(o.status==="dirty"&&t.dirty(),i(o.value),{status:t.value,value:o.value})}else return this._def.schema._parseAsync({data:s.data,path:s.path,parent:s}).then(o=>o.status==="aborted"?m:(o.status==="dirty"&&t.dirty(),i(o.value).then(()=>({status:t.value,value:o.value}))))}if(a.type==="transform")if(s.common.async===!1){const i=this._def.schema._parseSync({data:s.data,path:s.path,parent:s});if(!L(i))return m;const o=a.transform(i.value,n);if(o instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:o}}else return this._def.schema._parseAsync({data:s.data,path:s.path,parent:s}).then(i=>L(i)?Promise.resolve(a.transform(i.value,n)).then(o=>({status:t.value,value:o})):m);x.assertNever(a)}}D.create=(r,e,t)=>new D({schema:r,typeName:p.ZodEffects,effect:e,...g(t)});D.createWithPreprocess=(r,e,t)=>new D({schema:e,effect:{type:"preprocess",transform:r},typeName:p.ZodEffects,...g(t)});class E extends _{_parse(e){return this._getType(e)===u.undefined?O(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}E.create=(r,e)=>new E({innerType:r,typeName:p.ZodOptional,...g(e)});class U extends _{_parse(e){return this._getType(e)===u.null?O(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}U.create=(r,e)=>new U({innerType:r,typeName:p.ZodNullable,...g(e)});class re extends _{_parse(e){const{ctx:t}=this._processInputParams(e);let s=t.data;return t.parsedType===u.undefined&&(s=this._def.defaultValue()),this._def.innerType._parse({data:s,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}re.create=(r,e)=>new re({innerType:r,typeName:p.ZodDefault,defaultValue:typeof e.default=="function"?e.default:()=>e.default,...g(e)});class ae extends _{_parse(e){const{ctx:t}=this._processInputParams(e),s={...t,common:{...t.common,issues:[]}},a=this._def.innerType._parse({data:s.data,path:s.path,parent:{...s}});return Y(a)?a.then(n=>({status:"valid",value:n.status==="valid"?n.value:this._def.catchValue({get error(){return new A(s.common.issues)},input:s.data})})):{status:"valid",value:a.status==="valid"?a.value:this._def.catchValue({get error(){return new A(s.common.issues)},input:s.data})}}removeCatch(){return this._def.innerType}}ae.create=(r,e)=>new ae({innerType:r,typeName:p.ZodCatch,catchValue:typeof e.catch=="function"?e.catch:()=>e.catch,...g(e)});class Oe extends _{_parse(e){if(this._getType(e)!==u.nan){const s=this._getOrReturnCtx(e);return c(s,{code:d.invalid_type,expected:u.nan,received:s.parsedType}),m}return{status:"valid",value:e.data}}}Oe.create=r=>new Oe({typeName:p.ZodNaN,...g(r)});class lt extends _{_parse(e){const{ctx:t}=this._processInputParams(e),s=t.data;return this._def.type._parse({data:s,path:t.path,parent:t})}unwrap(){return this._def.type}}class ie extends _{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.common.async)return(async()=>{const n=await this._def.in._parseAsync({data:s.data,path:s.path,parent:s});return n.status==="aborted"?m:n.status==="dirty"?(t.dirty(),F(n.value)):this._def.out._parseAsync({data:n.value,path:s.path,parent:s})})();{const a=this._def.in._parseSync({data:s.data,path:s.path,parent:s});return a.status==="aborted"?m:a.status==="dirty"?(t.dirty(),{status:"dirty",value:a.value}):this._def.out._parseSync({data:a.value,path:s.path,parent:s})}}static create(e,t){return new ie({in:e,out:t,typeName:p.ZodPipeline})}}class ne extends _{_parse(e){const t=this._def.innerType._parse(e),s=a=>(L(a)&&(a.value=Object.freeze(a.value)),a);return Y(t)?t.then(a=>s(a)):s(t)}unwrap(){return this._def.innerType}}ne.create=(r,e)=>new ne({innerType:r,typeName:p.ZodReadonly,...g(e)});var p;(function(r){r.ZodString="ZodString",r.ZodNumber="ZodNumber",r.ZodNaN="ZodNaN",r.ZodBigInt="ZodBigInt",r.ZodBoolean="ZodBoolean",r.ZodDate="ZodDate",r.ZodSymbol="ZodSymbol",r.ZodUndefined="ZodUndefined",r.ZodNull="ZodNull",r.ZodAny="ZodAny",r.ZodUnknown="ZodUnknown",r.ZodNever="ZodNever",r.ZodVoid="ZodVoid",r.ZodArray="ZodArray",r.ZodObject="ZodObject",r.ZodUnion="ZodUnion",r.ZodDiscriminatedUnion="ZodDiscriminatedUnion",r.ZodIntersection="ZodIntersection",r.ZodTuple="ZodTuple",r.ZodRecord="ZodRecord",r.ZodMap="ZodMap",r.ZodSet="ZodSet",r.ZodFunction="ZodFunction",r.ZodLazy="ZodLazy",r.ZodLiteral="ZodLiteral",r.ZodEnum="ZodEnum",r.ZodEffects="ZodEffects",r.ZodNativeEnum="ZodNativeEnum",r.ZodOptional="ZodOptional",r.ZodNullable="ZodNullable",r.ZodDefault="ZodDefault",r.ZodCatch="ZodCatch",r.ZodPromise="ZodPromise",r.ZodBranded="ZodBranded",r.ZodPipeline="ZodPipeline",r.ZodReadonly="ZodReadonly"})(p||(p={}));const Ne=j.create;V.create;N.create;const ft=k.create;G.create;Q.create;P.create;z.create;X.create;E.create;U.create;const Ae=ft({username:Ne().min(1,"Username is required").min(3,"Username must be at least 3 characters").max(50,"Username must be less than 50 characters"),password:Ne().min(1,"Password is required").min(3,"Password must be at least 3 characters")});function ht(r,e){const t=r.safeParse(e);if(t.success)return{success:!0,data:t.data};const s={};return t.error.errors.forEach(a=>{const n=a.path.join(".");s[n]=a.message}),{success:!1,errors:s}}const mt={class:"container"},pt={key:0},gt={key:1},yt={key:2},_t={key:3},vt={class:"form-group"},xt={key:0,class:"field-error"},kt={class:"form-group"},bt={key:0,class:"field-error"},wt={key:0,class:"error"},Ct=["disabled"],St=je({__name:"index",setup(r){const e=Ee(),t=Ve(()=>e.user.value),s=Le(),a=ue({username:"",password:""}),n=ue({}),i=le(""),o=le(!1),f=w=>{const v=Ae.shape[w];if(!v)return;const T=v.safeParse(a[w]);T.success?delete n[w]:n[w]=T.error.errors[0]?.message||"Invalid value"},h=w=>{delete n[w]},y=async()=>{i.value="",Object.keys(n).forEach(v=>{delete n[v]});const w=ht(Ae,a);if(!w.success){Object.assign(n,w.errors);return}o.value=!0;try{if(await e.login(a.username,a.password)){s?.setUserId(a.username);const T=new Pe;T.set("username",a.username),s?.identify(T),s?.track("user_logged_in",{username:a.username}),a.username="",a.password="",await Me("/")}else i.value="Login failed. Please check your credentials and try again."}catch(v){console.error("Login failed:",v),i.value="An error occurred during login. Please try again."}finally{o.value=!1}};return(w,v)=>(Z(),R("div",mt,[b(t)?(Z(),R("h1",pt,"Welcome back, "+B(b(t).username)+"!",1)):(Z(),R("h1",gt,"Welcome to Burrito Consideration App")),b(t)?(Z(),R("div",yt,[...v[6]||(v[6]=[C("p",null,"You are now logged in. Feel free to explore:",-1),C("ul",null,[C("li",null,"Consider the potential of burritos"),C("li",null,"View your profile and statistics")],-1)])])):(Z(),R("div",_t,[v[9]||(v[9]=C("p",null,"Please sign in to begin your burrito journey",-1)),C("form",{onSubmit:$e(y,["prevent"]),class:"form",novalidate:""},[C("div",vt,[v[7]||(v[7]=C("label",{for:"username"},"Username:",-1)),oe(C("input",{id:"username","onUpdate:modelValue":v[0]||(v[0]=T=>b(a).username=T),type:"text",placeholder:"Enter any username",class:ce({"error-input":b(n).username}),onBlur:v[1]||(v[1]=T=>f("username")),onInput:v[2]||(v[2]=T=>h("username"))},null,34),[[de,b(a).username]]),b(n).username?(Z(),R("p",xt,B(b(n).username),1)):K("",!0)]),C("div",kt,[v[8]||(v[8]=C("label",{for:"password"},"Password:",-1)),oe(C("input",{id:"password","onUpdate:modelValue":v[3]||(v[3]=T=>b(a).password=T),type:"password",placeholder:"Enter any password",class:ce({"error-input":b(n).password}),onBlur:v[4]||(v[4]=T=>f("password")),onInput:v[5]||(v[5]=T=>h("password"))},null,34),[[de,b(a).password]]),b(n).password?(Z(),R("p",bt,B(b(n).password),1)):K("",!0)]),b(i)?(Z(),R("p",wt,B(b(i)),1)):K("",!0),C("button",{type:"submit",class:"btn-primary",disabled:b(o)},B(b(o)?"Signing in...":"Sign In"),9,Ct)],32),v[10]||(v[10]=C("p",{class:"note"}," Note: This is a demo app. Use any username and password to sign in. ",-1))]))]))}});export{St as default}; - -``` - ---- - -## .output/public/_nuxt/CaxW3SQp.js - -```js -import{_ as s,o as a,c as i,a as t,t as n,b as u,w as c,d as l,e as d}from"./BpsV6fwO.js";import{u as f}from"./Cvlh8XCj.js";const p={class:"antialiased bg-white dark:bg-[#020420] dark:text-white font-sans grid min-h-screen overflow-hidden place-content-center text-[#020420] tracking-wide"},m={class:"max-w-520px text-center"},h=["textContent"],b=["textContent"],x=["textContent"],g={class:"flex items-center justify-center w-full"},y={__name:"error-404",props:{appName:{type:String,default:"Nuxt"},status:{type:Number,default:404},statusText:{type:String,default:"Page not found"},description:{type:String,default:"Sorry, the page you are looking for could not be found."},backHome:{type:String,default:"Go back home"}},setup(e){const r=e;return f({title:`${r.status} - ${r.statusText} | ${r.appName}`,script:[{innerHTML:`!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver(e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)"LINK"===e.tagName&&"modulepreload"===e.rel&&r(e)}).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),"use-credentials"===e.crossOrigin?r.credentials="include":"anonymous"===e.crossOrigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}();`}],style:[{innerHTML:'*,:after,:before{border-color:var(--un-default-border-color,#e5e7eb);border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--un-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}h1,h2{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}h1,h2,p{margin:0}*,:after,:before{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 transparent;--un-ring-shadow:0 0 transparent;--un-shadow-inset: ;--un-shadow:0 0 transparent;--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgba(147,197,253,.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: }'}]}),(k,w)=>{const o=d;return a(),i("div",p,[t("div",m,[t("h1",{class:"font-semibold leading-none mb-4 sm:text-[110px] tabular-nums text-[80px]",textContent:n(e.status)},null,8,h),t("h2",{class:"font-semibold mb-2 sm:text-3xl text-2xl",textContent:n(e.statusText)},null,8,b),t("p",{class:"mb-4 px-2 text-[#64748B] text-md",textContent:n(e.description)},null,8,x),t("div",g,[u(o,{to:"/",class:"font-medium hover:text-[#00DC82] text-sm underline underline-offset-3"},{default:c(()=>[l(n(e.backHome),1)]),_:1})])])])}}},N=s(y,[["__scopeId","data-v-f09f9a20"]]);export{N as default}; - -``` - ---- - -## .output/public/_nuxt/CrxCT0gX.js - -```js -import{B as F,C as x,D as z}from"./BpsV6fwO.js";var S=10240,A=["image/","audio/","video/","application/octet-stream","font/"];function q(e){return e?A.some(function(n){return e.toLowerCase().startsWith(n)}):!1}function N(e){if(e!=null){if(typeof e=="string")return e;if(e instanceof URLSearchParams)return e.toString();if(e instanceof FormData){var n=[];return e.forEach(function(t,i){n.push("".concat(i,"=").concat(typeof t=="string"?t:"[File]"))}),n.join("&")}}}function k(e,n){if(new Blob([e]).size<=n)return{value:e,truncated:!1};for(var t=0,i=Math.min(e.length,n);t0&&e.charCodeAt(t-1)>=55296&&e.charCodeAt(t-1)<=56319&&(t-=1),{value:e.slice(0,t),truncated:!0}}var R=(function(){function e(){this.fetchObserver=null}return e.prototype.start=function(n,t){this.eventCallback=n,this.networkConfig=t,this.observeFetch()},e.prototype.stop=function(){var n;(n=this.fetchObserver)===null||n===void 0||n.call(this),this.fetchObserver=null,this.eventCallback=void 0,this.networkConfig=void 0},e.prototype.notifyEvent=function(n){var t;(t=this.eventCallback)===null||t===void 0||t.call(this,n)},e.prototype.observeFetch=function(){var n=this,t=z();if(t){var i=t.fetch;i&&(t.fetch=function(s,o){return F(n,void 0,void 0,function(){var d,r,a,h,b,u,l,v,w,_,p,l,y,g=this,m,E;return x(this,function(f){switch(f.label){case 0:d=Date.now(),r={timestamp:d,type:"fetch",method:o?.method||"GET",url:s.toString(),requestHeaders:o?.headers},a=(m=this.networkConfig)===null||m===void 0?void 0:m.body,a?.request&&(h=N(o?.body),h!==void 0&&(b=(E=a.maxBodySizeBytes)!==null&&E!==void 0?E:S,r.requestBody=k(h,b).value)),f.label=1;case 1:return f.trys.push([1,3,,4]),[4,i(s,o)];case 2:return u=f.sent(),l=Date.now(),r.status=u.status,r.duration=l-d,v={},u.headers.forEach(function(B,c){v[c]=B}),r.responseHeaders=v,a?.response?(w=v["content-type"]||null,q(w)?(r.responseBodyStatus="skipped_binary",this.notifyEvent(r)):(_=u.clone(),_.text().then(function(B){var c,T=(c=a.maxBodySizeBytes)!==null&&c!==void 0?c:S,C=k(B,T),O=C.value,D=C.truncated;r.responseBody=O,r.responseBodyStatus=D?"truncated":"captured",g.notifyEvent(r)},function(){r.responseBodyStatus="error",g.notifyEvent(r)}))):this.notifyEvent(r),[2,u];case 3:throw p=f.sent(),l=Date.now(),r.duration=l-d,y=p,r.error={name:y.name||"UnknownError",message:y.message||"An unknown error occurred"},this.notifyEvent(r),p;case 4:return[2]}})})},this.fetchObserver=function(){t.fetch=i})}},e})();export{R as NetworkObservers}; - -``` - ---- - -## .output/public/_nuxt/Cvlh8XCj.js - -```js -import{u as a,f as s,h as u,i as r,g as h}from"./BpsV6fwO.js";function i(t){const e=t||s();return e.ssrContext?.head||e.runWithContext(()=>{if(u()){const n=r(h);if(!n)throw new Error("[nuxt] [unhead] Missing Unhead instance.");return n}})}function d(t,e={}){const n=e.head||i(e.nuxt);return a(t,{head:n,...e})}export{d as u}; - -``` - ---- - -## .output/public/_nuxt/D7Bf6Say.js - -```js -var r=`var WebWorker=function(r){"use strict";var n=Uint8Array,e=Uint16Array,f=Uint32Array,t=new n([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),a=new n([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),o=new n([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),i=function(r,n){for(var t=new e(31),a=0;a<31;++a)t[a]=n+=1<>>1|(21845&h)<<1;g=(61680&(g=(52428&g)>>>2|(13107&g)<<2))>>>4|(3855&g)<<4,c[h]=((65280&g)>>>8|(255&g)<<8)>>>1}var w=function(r,n,f){for(var t=r.length,a=0,o=new e(n);a>>u]=s}else for(i=new e(t),a=0;a>>15-r[a];return i},d=new n(288);for(h=0;h<144;++h)d[h]=8;for(h=144;h<256;++h)d[h]=9;for(h=256;h<280;++h)d[h]=7;for(h=280;h<288;++h)d[h]=8;var m=new n(32);for(h=0;h<32;++h)m[h]=5;var y=w(d,9,0),M=w(m,5,0),p=function(r){return(r/8|0)+(7&r&&1)},b=function(r,t,a){(null==a||a>r.length)&&(a=r.length);var o=new(r instanceof e?e:r instanceof f?f:n)(a-t);return o.set(r.subarray(t,a)),o},C=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8},S=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8,r[f+2]|=e>>>16},x=function(r,f){for(var t=[],a=0;ag&&(g=i[a].s);var w=new e(g+1),d=A(t[c-1],w,0);if(d>f){a=0;var m=0,y=d-f,M=1<f))break;m+=M-(1<>>=y;m>0;){var b=i[a].s;w[b]=0&&m;--a){var C=i[a].s;w[C]==f&&(--w[C],++m)}d=f}return[new n(w),d]},A=function(r,n,e){return-1==r.s?Math.max(A(r.l,n,e+1),A(r.r,n,e+1)):n[r.s]=e},O=function(r){for(var n=r.length;n&&!r[--n];);for(var f=new e(++n),t=0,a=r[0],o=1,i=function(r){f[t++]=r},v=1;v<=n;++v)if(r[v]==a&&v!=n)++o;else{if(!a&&o>2){for(;o>138;o-=138)i(32754);o>2&&(i(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(i(a),--o;o>6;o-=6)i(8304);o>2&&(i(o-3<<5|8208),o=0)}for(;o--;)i(a);o=1,a=r[v]}return[f.subarray(0,t),n]},T=function(r,n){for(var e=0,f=0;f>>8,r[t+2]=255^r[t],r[t+3]=255^r[t+1];for(var a=0;a4&&!B[o[G-1]];--G);var H,K,L,Q,R=h+5<<3,V=T(v,d)+T(u,m)+s,X=T(v,b)+T(u,J)+s+14+3*G+T(P,B)+(2*P[16]+3*P[17]+7*P[18]);if(R<=V&&R<=X)return k(n,g,r.subarray(c,c+h));if(C(n,g,1+(X15&&(C(n,g,rr[q]>>>5&127),g+=rr[q]>>>12)}}}else H=y,K=d,L=M,Q=m;for(q=0;q255){nr=i[q]>>>18&31;S(n,g,H[nr+257]),g+=K[nr+257],nr>7&&(C(n,g,i[q]>>>23&31),g+=t[nr]);var er=31&i[q];S(n,g,L[er]),g+=Q[er],er>3&&(S(n,g,i[q]>>>5&8191),g+=a[er])}else S(n,g,H[i[q]]),g+=K[i[q]];return S(n,g,H[256]),g+K[256]},J=new f([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),N=function(r,o,i,v,u){return function(r,o,i,v,u,c){var h=r.length,g=new n(v+h+5*(1+Math.floor(h/7e3))+u),w=g.subarray(v,g.length-u),d=0;if(!o||h<8)for(var m=0;m<=h;m+=65535){var y=m+65535;y>>13,S=8191&M,x=(1<7e3||P>24576)&&H>423){d=E(r,w,0,D,I,W,j,P,z,m-z,d),P=_=j=0,z=m;for(var K=0;K<286;++K)I[K]=0;for(K=0;K<30;++K)W[K]=0}var L=2,Q=0,R=S,V=F-G&32767;if(H>2&&B==U(m-V))for(var X=Math.min(C,H)-1,Y=Math.min(32767,m),Z=Math.min(258,H);V<=Y&&--R&&F!=G;){if(r[m+L]==r[m+L-V]){for(var $=0;$L){if(L=$,Q=V,$>X)break;var rr=Math.min(V,$-2),nr=0;for(K=0;Knr&&(nr=fr,G=er)}}}V+=(F=G)-(G=A[F])+32768&32767}if(Q){D[P++]=268435456|s[L]<<18|l[Q];var tr=31&s[L],ar=31&l[Q];j+=t[tr]+a[ar],++I[257+tr],++W[ar],q=m+L,++_}else D[P++]=r[m],++I[r[m]]}}d=E(r,w,c,D,I,W,j,P,z,m-z,d)}return b(g,0,v+p(d)+u)}(r,null==o.level?6:o.level,null==o.mem?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(r.length)))):12+o.mem,i,v,!0)};function U(r,n){void 0===n&&(n={});var e=function(){var r=1,n=0;return{p:function(e){for(var f=r,t=n,a=e.length,o=0;o!=a;){for(var i=Math.min(o+5552,a);o>>8<<16|(255&n)<<8|n>>>8)+2*((255&r)<<23)}}}();e.p(r);var f,t,a,o=N(r,n,2,4);return f=o,t=n.level,a=0==t?0:t<6?1:9==t?3:2,f[0]=120,f[1]=a<<6|(a?32-2*a:1),function(r,n,e){for(;e;++n)r[n]=e,e>>>=8}(o,o.length-4,e.d()),o}const D=r=>{const e={...r,v:"v1"};return function(r,n){var e="";if(!n&&"undefined"!=typeof TextDecoder)return(new TextDecoder).decode(r);for(var f=0;f>10,56320|1023&t))}return e}(U(function(r,e){var f=r.length;if(!e&&"undefined"!=typeof TextEncoder)return(new TextEncoder).encode(r);for(var t=new n(r.length+(r.length>>>1)),a=0,o=function(r){t[a++]=r},i=0;it.length){var v=new n(a+8+(f-i<<1));v.set(t),t=v}var u=r.charCodeAt(i);u<128||e?o(u):u<2048?(o(192|u>>>6),o(128|63&u)):u>55295&&u<57344?(o(240|(u=65536+(1047552&u)|1023&r.charCodeAt(++i))>>>18),o(128|u>>>12&63),o(128|u>>>6&63),o(128|63&u)):(o(224|u>>>12),o(128|u>>>6&63),o(128|63&u))}return b(t,0,a)}(JSON.stringify(e))),!0)};onmessage=r=>{const{event:n,sessionId:e}="string"==typeof r.data?JSON.parse(r.data):r.data,f=JSON.stringify(D(n));postMessage({compressedEvent:f,sessionId:e})};const I=onmessage;return r.compressionOnMessage=I,Object.defineProperty(r,"__esModule",{value:!0}),r}({}); -`;export{r as compressionScript}; - -``` - ---- - -## .output/public/_nuxt/DkWg1RwU.js - -```js -import{j as d,k as l,c as s,a as t,d as i,t as u,l as r,o as n,r as p}from"./BpsV6fwO.js";const m={class:"container"},_={class:"stats"},c={style:{"margin-top":"2rem"}},b={key:0},g={key:1},y={key:2},f={key:3},h={key:4},v=d({__name:"profile",setup(k){const a=l(),e=p(()=>a.user.value);return(C,o)=>(n(),s("div",m,[o[4]||(o[4]=t("h1",null,"User Profile",-1)),t("div",_,[o[2]||(o[2]=t("h2",null,"Your Information",-1)),t("p",null,[o[0]||(o[0]=t("strong",null,"Username:",-1)),i(" "+u(r(e)?.username),1)]),t("p",null,[o[1]||(o[1]=t("strong",null,"Burrito Considerations:",-1)),i(" "+u(r(e)?.burritoConsiderations),1)])]),t("div",c,[o[3]||(o[3]=t("h3",null,"Your Burrito Journey",-1)),r(e)?.burritoConsiderations===0?(n(),s("p",b," You haven't considered any burritos yet. Visit the Burrito Consideration page to start! ")):r(e)?.burritoConsiderations===1?(n(),s("p",g," You've considered the burrito potential once. Keep going! ")):r(e)&&r(e).burritoConsiderations<5?(n(),s("p",y," You're getting the hang of burrito consideration! ")):r(e)&&r(e).burritoConsiderations<10?(n(),s("p",f," You're becoming a burrito consideration expert! ")):(n(),s("p",h,"You are a true burrito consideration master! 🌯"))])]))}});export{v as default}; - -``` - ---- - -## .output/public/_nuxt/DMrp4Fbu.js - -```js -var rt={},on=Object.defineProperty,an=(e,t,r)=>t in e?on(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t,r)=>an(e,typeof t!="symbol"?t+"":t,r),Jr,ln=Object.defineProperty,un=(e,t,r)=>t in e?ln(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Yr=(e,t,r)=>un(e,typeof t!="symbol"?t+"":t,r),Y=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Y||{});const Qr={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},Xr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},je={};function cn(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function kr(e){if(je[e])return je[e];const t=cn(e)||globalThis[e],r=t.prototype,l=e in Qr?Qr[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in Xr?Xr[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return je[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?je[e]=p:r}catch{return r}}const Ct={};function be(e,t,r){var l;const s=`${e}.${String(r)}`;if(Ct[s])return Ct[s].call(t);const f=kr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Ct[s]=u,u.call(t)):t[r]}const xt={};function wi(e,t,r){const l=`${e}.${String(r)}`;if(xt[l])return xt[l].bind(t);const f=kr(e)[r];return typeof f!="function"?t[r]:(xt[l]=f,f.bind(t))}function hn(e){return be("Node",e,"childNodes")}function fn(e){return be("Node",e,"parentNode")}function pn(e){return be("Node",e,"parentElement")}function dn(e){return be("Node",e,"textContent")}function mn(e,t){return wi("Node",e,"contains")(t)}function gn(e){return wi("Node",e,"getRootNode")()}function yn(e){return!e||!("host"in e)?null:be("ShadowRoot",e,"host")}function wn(e){return e.styleSheets}function bn(e){return!e||!("shadowRoot"in e)?null:be("Element",e,"shadowRoot")}function Sn(e,t){return be("Element",e,"querySelector")(t)}function vn(e,t){return be("Element",e,"querySelectorAll")(t)}function Cn(){return kr("MutationObserver").constructor}const K={childNodes:hn,parentNode:fn,parentElement:pn,textContent:dn,contains:mn,getRootNode:gn,host:yn,styleSheets:wn,shadowRoot:bn,querySelector:Sn,querySelectorAll:vn,mutationObserver:Cn};function bi(e){return e.nodeType===e.ELEMENT_NODE}function Te(e){const t=e&&"host"in e&&"mode"in e&&K.host(e)||null;return!!(t&&"shadowRoot"in t&&K.shadowRoot(t)===e)}function Fe(e){return Object.prototype.toString.call(e)==="[object ShadowRoot]"}function xn(e){return e.includes(" background-clip: text;")&&!e.includes(" -webkit-background-clip: text;")&&(e=e.replace(/\sbackground-clip:\s*text;/g," -webkit-background-clip: text; background-clip: text;")),e}function Rn(e){const{cssText:t}=e;if(t.split('"').length<3)return t;const r=["@import",`url(${JSON.stringify(e.href)})`];return e.layerName===""?r.push("layer"):e.layerName&&r.push(`layer(${e.layerName})`),e.supportsText&&r.push(`supports(${e.supportsText})`),e.media.length&&r.push(e.media.mediaText),r.join(" ")+";"}function Mr(e){try{const t=e.rules||e.cssRules;if(!t)return null;let r=e.href;!r&&e.ownerNode&&e.ownerNode.ownerDocument&&(r=e.ownerNode.ownerDocument.location.href);const l=Array.from(t,s=>Si(s,r)).join("");return xn(l)}catch{return null}}function Si(e,t){if(Mn(e)){let r;try{r=Mr(e.styleSheet)||Rn(e)}catch{r=e.cssText}return e.styleSheet.href?nt(r,e.styleSheet.href):r}else{let r=e.cssText;return En(e)&&e.selectorText.includes(":")&&(r=On(r)),t?nt(r,t):r}}function On(e){const t=/(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;return e.replace(t,"$1\\$2")}function Mn(e){return"styleSheet"in e}function En(e){return"selectorText"in e}class vi{constructor(){Yr(this,"idNodeMap",new Map),Yr(this,"nodeMetaMap",new WeakMap)}getId(t){var r;return t?((r=this.getMeta(t))==null?void 0:r.id)??-1:-1}getNode(t){return this.idNodeMap.get(t)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(t){return this.nodeMetaMap.get(t)||null}removeNodeFromMap(t){const r=this.getId(t);this.idNodeMap.delete(r),t.childNodes&&t.childNodes.forEach(l=>this.removeNodeFromMap(l))}has(t){return this.idNodeMap.has(t)}hasNode(t){return this.nodeMetaMap.has(t)}add(t,r){const l=r.id;this.idNodeMap.set(l,t),this.nodeMetaMap.set(t,r)}replace(t,r){const l=this.getNode(t);if(l){const s=this.nodeMetaMap.get(l);s&&this.nodeMetaMap.set(r,s)}this.idNodeMap.set(t,r)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function In(){return new vi}function st({element:e,maskInputOptions:t,tagName:r,type:l,value:s,maskInputFn:f}){let u=s||"";const m=l&&Ce(l);return(t[r.toLowerCase()]||m&&t[m])&&(f?u=f(u,e):u="*".repeat(u.length)),u}function Ce(e){return e.toLowerCase()}const Kr="__rrweb_original__";function An(e){const t=e.getContext("2d");if(!t)return!0;const r=50;for(let l=0;la!==0))return!1}return!0}function it(e){const t=e.type;return e.hasAttribute("data-rr-is-password")?"password":t?Ce(t):null}function Ci(e,t){let r;try{r=new URL(e,t??window.location.href)}catch{return null}const l=/\.([0-9a-z]+)(?:$)/i,s=r.pathname.match(l);return s?.[1]??null}function kn(e){let t="";return e.indexOf("//")>-1?t=e.split("/").slice(0,3).join("/"):t=e.split("/")[0],t=t.split("?")[0],t}const Nn=/url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm,Pn=/^(?:[a-z+]+:)?\/\//i,_n=/^www\..*/i,Ln=/^(data:)([^,]*),(.*)/i;function nt(e,t){return(e||"").replace(Nn,(r,l,s,f,u,m)=>{const a=s||u||m,p=l||f||"";if(!a)return r;if(Pn.test(a)||_n.test(a))return`url(${p}${a}${p})`;if(Ln.test(a))return`url(${p}${a}${p})`;if(a[0]==="/")return`url(${p}${kn(t)+a}${p})`;const i=t.split("/"),c=a.split("/");i.pop();for(const o of c)o!=="."&&(o===".."?i.pop():i.push(o));return`url(${p}${i.join("/")}${p})`})}function He(e,t=!1){return t?e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,""):e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,"").replace(/0px/g,"0")}function Dn(e,t,r=!1){const l=Array.from(t.childNodes),s=[];let f=0;if(l.length>1&&e&&typeof e=="string"){let u=He(e,r);const m=u.length/e.length;for(let a=1;a2&&n[0]===""&&l[a-1].textContent!=="")d=u.indexOf(o,1);else if(n.length===1){if(o=o.substring(0,o.length-1),n=u.split(o),n.length<=1)return s.push(e),s;c=i+1}else c===p.length-1&&(d=u.indexOf(o));if(n.length>=2&&c>i){const h=l[a-1].textContent;if(h&&typeof h=="string"){const y=He(h).length;d=u.indexOf(o,y)}d===-1&&(d=n[0].length)}if(d!==-1){let h=Math.floor(d/m);for(;h>0&&h50*l.length)return s.push(e),s;const y=He(e.substring(0,h),r);if(y.length===d){s.push(e.substring(0,h)),e=e.substring(h),u=u.substring(d);break}else y.length=t.length);){let f=l(zn);if(f.slice(-1)===",")f=ke(e,f.substring(0,f.length-1)),s.push(f);else{let u="";f=ke(e,f);let m=!1;for(;;){const a=t.charAt(r);if(a===""){s.push((f+u).trim());break}else if(m)a===")"&&(m=!1);else if(a===","){r+=1,s.push((f+u).trim());break}else a==="("&&(m=!0);u+=a,r+=1}}}return s.join(", ")}const es=new WeakMap;function ke(e,t){return!t||t.trim()===""?t:Nr(e,t)}function jn(e){return!!(e.tagName==="svg"||e.ownerSVGElement)}function Nr(e,t){let r=es.get(e);if(r||(r=e.createElement("a"),es.set(e,r)),!t)t="";else if(t.startsWith("blob:")||t.startsWith("data:"))return t;return r.setAttribute("href",t),r.href}function Ri(e,t,r,l){return l&&(r==="src"||r==="href"&&!(t==="use"&&l[0]==="#")||r==="xlink:href"&&l[0]!=="#"||r==="background"&&(t==="table"||t==="td"||t==="th")?ke(e,l):r==="srcset"?qn(e,l):r==="style"?nt(l,Nr(e)):t==="object"&&r==="data"?ke(e,l):l)}function Oi(e,t,r){return(e==="video"||e==="audio")&&t==="autoplay"}function Hn(e,t,r){try{if(typeof t=="string"){if(e.classList.contains(t))return!0}else for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}if(r)return e.matches(r)}catch{}return!1}function ot(e,t,r){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return r?ot(K.parentNode(e),t,r):!1;for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}return r?ot(K.parentNode(e),t,r):!1}function Mi(e,t,r,l){let s;if(bi(e)){if(s=e,!K.childNodes(s).length)return!1}else{if(K.parentElement(e)===null)return!1;s=K.parentElement(e)}try{if(typeof t=="string"){if(l){if(s.closest(`.${t}`))return!0}else if(s.classList.contains(t))return!0}else if(ot(s,t,l))return!0;if(r){if(l){if(s.closest(r))return!0}else if(s.matches(r))return!0}}catch{}return!1}function Gn(e,t,r){const l=e.contentWindow;if(!l)return;let s=!1,f;try{f=l.document.readyState}catch{return}if(f!=="complete"){const m=setTimeout(()=>{s||(t(),s=!0)},r);e.addEventListener("load",()=>{clearTimeout(m),s=!0,t()});return}const u="about:blank";if(l.location.href!==u||e.src===u||e.src==="")return setTimeout(t,0),e.addEventListener("load",t);e.addEventListener("load",t)}function Vn(e,t,r){let l=!1,s;try{s=e.sheet}catch{return}if(s)return;const f=setTimeout(()=>{l||(t(),l=!0)},r);e.addEventListener("load",()=>{clearTimeout(f),l=!0,t()})}function Jn(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:u,inlineStylesheet:m,maskInputOptions:a={},maskTextFn:p,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o={},inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y=!1,cssCaptured:C=!1,applyBackgroundColorToBlockedElements:w=!1}=t,S=Yn(r,l);switch(e.nodeType){case e.DOCUMENT_NODE:return e.compatMode!=="CSS1Compat"?{type:Y.Document,childNodes:[],compatMode:e.compatMode}:{type:Y.Document,childNodes:[]};case e.DOCUMENT_TYPE_NODE:return{type:Y.DocumentType,name:e.name,publicId:e.publicId,systemId:e.systemId,rootId:S};case e.ELEMENT_NODE:return Xn(e,{doc:r,blockClass:s,blockSelector:f,inlineStylesheet:m,maskInputOptions:a,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o,inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y,rootId:S,applyBackgroundColorToBlockedElements:w});case e.TEXT_NODE:return Qn(e,{doc:r,needsMask:u,maskTextFn:p,rootId:S,cssCaptured:C});case e.CDATA_SECTION_NODE:return{type:Y.CDATA,textContent:"",rootId:S};case e.COMMENT_NODE:return{type:Y.Comment,textContent:K.textContent(e)||"",rootId:S};default:return!1}}function Yn(e,t){if(!t.hasNode(e))return;const r=t.getId(e);return r===1?void 0:r}function Qn(e,t){const{needsMask:r,maskTextFn:l,rootId:s,cssCaptured:f}=t,u=K.parentNode(e),m=u&&u.tagName;let a="";const p=m==="STYLE"?!0:void 0,i=m==="SCRIPT"?!0:void 0;return i?a="SCRIPT_PLACEHOLDER":f||(a=K.textContent(e),p&&a&&(a=nt(a,Nr(t.doc)))),!p&&!i&&a&&r&&(a=l?l(a,K.parentElement(e)):a.replace(/[\S]/g,"*")),{type:Y.Text,textContent:a||"",rootId:s}}function Xn(e,t){const{doc:r,blockClass:l,blockSelector:s,inlineStylesheet:f,maskInputOptions:u={},maskInputFn:m,maskAttributeFn:a,dataURLOptions:p={},inlineImages:i,recordCanvas:c,keepIframeSrcFn:o,newlyAddedElement:n=!1,rootId:d,applyBackgroundColorToBlockedElements:h=!1}=t,y=Hn(e,l,s),C=Bn(e);let w={};const S=e.attributes.length;for(let g=0;gx.href===e.href);let v=null;g&&(v=Mr(g)),v&&(delete w.rel,delete w.href,w._cssText=v)}if(C==="style"&&e.sheet){let g=Mr(e.sheet);g&&(e.childNodes.length>1&&(g=Tn(g,e)),w._cssText=g)}if(C==="input"||C==="textarea"||C==="select"){const g=e.value,v=e.checked;w.type!=="radio"&&w.type!=="checkbox"&&w.type!=="submit"&&w.type!=="button"&&g?w.value=st({element:e,type:it(e),tagName:C,value:g,maskInputOptions:u,maskInputFn:m}):v&&(w.checked=v)}if(C==="option"&&(e.selected&&!u.select?w.selected=!0:delete w.selected),C==="dialog"&&e.open&&(w.rr_open_mode=e.matches("dialog:modal")?"modal":"non-modal"),C==="canvas"&&c){if(e.__context==="2d")An(e)||(w.rr_dataURL=e.toDataURL(p.type,p.quality));else if(!("__context"in e)){const g=e.toDataURL(p.type,p.quality),v=r.createElement("canvas");v.width=e.width,v.height=e.height;const x=v.toDataURL(p.type,p.quality);g!==x&&(w.rr_dataURL=g)}}if(C==="img"&&i){Ee||(Ee=r.createElement("canvas"),Zr=Ee.getContext("2d"));const g=e,v=g.currentSrc||g.getAttribute("src")||"",x=g.crossOrigin,O=()=>{g.removeEventListener("load",O);try{Ee.width=g.naturalWidth,Ee.height=g.naturalHeight,Zr.drawImage(g,0,0),w.rr_dataURL=Ee.toDataURL(p.type,p.quality)}catch(A){if(g.crossOrigin!=="anonymous"){g.crossOrigin="anonymous",g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O);return}else console.warn(`Cannot inline img src=${v}! Error: ${A}`)}g.crossOrigin==="anonymous"&&(x?w.crossOrigin=x:g.removeAttribute("crossorigin"))};g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O)}if(C==="audio"||C==="video"){const g=w;g.rr_mediaState=e.paused?"paused":"played",g.rr_mediaCurrentTime=e.currentTime,g.rr_mediaPlaybackRate=e.playbackRate,g.rr_mediaMuted=e.muted,g.rr_mediaLoop=e.loop,g.rr_mediaVolume=e.volume}if(n||(e.scrollLeft&&(w.rr_scrollLeft=e.scrollLeft),e.scrollTop&&(w.rr_scrollTop=e.scrollTop)),y){const{width:g,height:v}=e.getBoundingClientRect();w={class:w.class,rr_width:`${g}px`,rr_height:`${v}px`,...h?{rr_background_color:Fn}:{}}}C==="iframe"&&!o(w.src)&&(e.contentDocument||(w.rr_src=w.src),delete w.src);let b;try{customElements.get(C)&&(b=!0)}catch{}return{type:Y.Element,tagName:C,attributes:w,childNodes:[],isSVG:jn(e)||void 0,needBlock:y,rootId:d,isCustom:b}}function z(e){return e==null?"":e.toLowerCase()}function Kn(e,t){if(t.comment&&e.type===Y.Comment)return!0;if(e.type===Y.Element){if(t.script&&(e.tagName==="script"||e.tagName==="link"&&(e.attributes.rel==="preload"||e.attributes.rel==="modulepreload")&&e.attributes.as==="script"||e.tagName==="link"&&e.attributes.rel==="prefetch"&&typeof e.attributes.href=="string"&&Ci(e.attributes.href)==="js"))return!0;if(t.headFavicon&&(e.tagName==="link"&&e.attributes.rel==="shortcut icon"||e.tagName==="meta"&&(z(e.attributes.name).match(/^msapplication-tile(image|color)$/)||z(e.attributes.name)==="application-name"||z(e.attributes.rel)==="icon"||z(e.attributes.rel)==="apple-touch-icon"||z(e.attributes.rel)==="shortcut icon")))return!0;if(e.tagName==="meta"){if(t.headMetaDescKeywords&&z(e.attributes.name).match(/^description|keywords$/))return!0;if(t.headMetaSocial&&(z(e.attributes.property).match(/^(og|twitter|fb):/)||z(e.attributes.name).match(/^(og|twitter):/)||z(e.attributes.name)==="pinterest"))return!0;if(t.headMetaRobots&&(z(e.attributes.name)==="robots"||z(e.attributes.name)==="googlebot"||z(e.attributes.name)==="bingbot"))return!0;if(t.headMetaHttpEquiv&&e.attributes["http-equiv"]!==void 0)return!0;if(t.headMetaAuthorship&&(z(e.attributes.name)==="author"||z(e.attributes.name)==="generator"||z(e.attributes.name)==="framework"||z(e.attributes.name)==="publisher"||z(e.attributes.name)==="progid"||z(e.attributes.property).match(/^article:/)||z(e.attributes.property).match(/^product:/)))return!0;if(t.headMetaVerification&&(z(e.attributes.name)==="google-site-verification"||z(e.attributes.name)==="yandex-verification"||z(e.attributes.name)==="csrf-token"||z(e.attributes.name)==="p:domain_verify"||z(e.attributes.name)==="verify-v1"||z(e.attributes.name)==="verification"||z(e.attributes.name)==="shopify-checkout-api-token"))return!0}}return!1}function Ne(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,maskTextClass:u,maskTextSelector:m,skipChild:a=!1,inlineStylesheet:p=!0,maskInputOptions:i={},maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h={},inlineImages:y=!1,recordCanvas:C=!1,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b=5e3,onStylesheetLoad:g,stylesheetLoadTimeout:v=5e3,keepIframeSrcFn:x=()=>!1,newlyAddedElement:O=!1,cssCaptured:A=!1,applyBackgroundColorToBlockedElements:M=!1}=t;let{needsMask:$}=t,{preserveWhiteSpace:k=!0}=t;$||($=Mi(e,u,m,$===void 0));const R=Jn(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,dataURLOptions:h,inlineImages:y,recordCanvas:C,keepIframeSrcFn:x,newlyAddedElement:O,cssCaptured:A,applyBackgroundColorToBlockedElements:M});if(!R)return console.warn(e,"not serialized"),null;let Z;l.hasNode(e)?Z=l.getId(e):Kn(R,d)||!k&&R.type===Y.Text&&!R.textContent.replace(/^\s+|\s+$/gm,"").length?Z=$e:Z=xi();const N=Object.assign(R,{id:Z});if(l.add(e,N),Z===$e)return null;w&&w(e);let ee=!a;if(N.type===Y.Element){ee=ee&&!N.needBlock,delete N.needBlock;const F=K.shadowRoot(e);F&&Fe(F)&&(N.isShadowHost=!0)}if((N.type===Y.Document||N.type===Y.Element)&&ee){d.headWhitespace&&N.type===Y.Element&&N.tagName==="head"&&(k=!1);const F={doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:a,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x,cssCaptured:!1,applyBackgroundColorToBlockedElements:M};if(!(N.type===Y.Element&&N.tagName==="textarea"&&N.attributes.value!==void 0)){N.type===Y.Element&&N.attributes._cssText!==void 0&&typeof N.attributes._cssText=="string"&&(F.cssCaptured=!0);for(const W of Array.from(K.childNodes(e))){const P=Ne(W,F);P&&N.childNodes.push(P)}}let Q=null;if(bi(e)&&(Q=K.shadowRoot(e)))for(const W of Array.from(K.childNodes(Q))){const P=Ne(W,F);P&&(Fe(Q)&&(P.isShadow=!0),N.childNodes.push(P))}}const H=K.parentNode(e);return H&&Te(H)&&Fe(H)&&(N.isShadow=!0),N.type===Y.Element&&N.tagName==="iframe"&&Gn(e,()=>{const F=e.contentDocument;if(F&&S){const Q=Ne(F,{doc:F,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});Q&&S(e,Q)}},b),N.type===Y.Element&&N.tagName==="link"&&typeof N.attributes.rel=="string"&&(N.attributes.rel==="stylesheet"||N.attributes.rel==="preload"&&typeof N.attributes.href=="string"&&Ci(N.attributes.href)==="css")&&Vn(e,()=>{if(g){const F=Ne(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});F&&g(e,F)}},v),N}function Zn(e,t){const{mirror:r=new vi,blockClass:l="rr-block",blockSelector:s=null,maskTextClass:f="rr-mask",maskTextSelector:u=null,inlineStylesheet:m=!0,inlineImages:a=!1,recordCanvas:p=!1,maskAllInputs:i=!1,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOM:d=!1,dataURLOptions:h,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v=()=>!1,applyBackgroundColorToBlockedElements:x=!1}=t||{};return Ne(e,{doc:e,mirror:r,blockClass:l,blockSelector:s,maskTextClass:f,maskTextSelector:u,skipChild:!1,inlineStylesheet:m,maskInputOptions:i===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:i===!1?{password:!0}:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d===!0||d==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:d==="all",headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0,headMetaVerification:!0}:d===!1?{}:d,dataURLOptions:h,inlineImages:a,recordCanvas:p,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v,newlyAddedElement:!1,applyBackgroundColorToBlockedElements:x})}function eo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function to(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Ge={exports:{}},ts;function ro(){if(ts)return Ge.exports;ts=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Ge.exports=t(),Ge.exports.createColors=t,Ge.exports}const so={},io=Object.freeze(Object.defineProperty({__proto__:null,default:so},Symbol.toStringTag,{value:"Module"})),he=to(io);var Rt,rs;function Pr(){if(rs)return Rt;rs=1;let e=ro(),t=he;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Rt=r,r.default=r,Rt}var Ve={},ss;function _r(){return ss||(ss=1,Ve.isClean=Symbol("isClean"),Ve.my=Symbol("my")),Ve}var Ot,is;function Ei(){if(is)return Ot;is=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Ot=r,r.default=r,Ot}var Mt,ns;function ct(){if(ns)return Mt;ns=1;let e=Ei();function t(r,l){new e(l).stringify(r)}return Mt=t,t.default=t,Mt}var Et,os;function ht(){if(os)return Et;os=1;let{isClean:e,my:t}=_r(),r=Pr(),l=Ei(),s=ct();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Et=u,u.default=u,Et}var It,as;function ft(){if(as)return It;as=1;let e=ht();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return It=t,t.default=t,It}var At,ls;function no(){if(ls)return At;ls=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return At={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},At}var kt,us;function Ii(){if(us)return kt;us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{existsSync:r,readFileSync:l}=he,{dirname:s,join:f}=he;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return kt=m,m.default=m,kt}var Nt,cs;function pt(){if(cs)return Nt;cs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{fileURLToPath:r,pathToFileURL:l}=he,{isAbsolute:s,resolve:f}=he,{nanoid:u}=no(),m=he,a=Pr(),p=Ii(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return Nt=n,n.default=n,m&&m.registerInput&&m.registerInput(n),Nt}var Pt,hs;function Ai(){if(hs)return Pt;hs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{dirname:r,relative:l,resolve:s,sep:f}=he,{pathToFileURL:u}=he,m=pt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return Pt=i,Pt}var _t,fs;function dt(){if(fs)return _t;fs=1;let e=ht();class t extends e{constructor(l){super(l),this.type="comment"}}return _t=t,t.default=t,_t}var Lt,ps;function xe(){if(ps)return Lt;ps=1;let{isClean:e,my:t}=_r(),r=ft(),l=dt(),s=ht(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},Lt=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},Lt}var Dt,ds;function Lr(){if(ds)return Dt;ds=1;let e=xe(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Dt=l,l.default=l,Dt}var Tt,ms;function ki(){if(ms)return Tt;ms=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Tt=e,e.default=e,Tt}var Ft,gs;function Dr(){if(gs)return Ft;gs=1;let e=ki();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return Ft=t,t.default=t,Ft}var Ut,ys;function oo(){if(ys)return Ut;ys=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return Ut=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},Ut}var $t,ws;function Tr(){if(ws)return $t;ws=1;let e=xe();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return $t=t,t.default=t,e.registerAtRule(t),$t}var Bt,bs;function ze(){if(bs)return Bt;bs=1;let e=xe(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Bt=l,l.default=l,e.registerRoot(l),Bt}var zt,Ss;function Ni(){if(Ss)return zt;Ss=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return zt=e,e.default=e,zt}var Wt,vs;function Fr(){if(vs)return Wt;vs=1;let e=xe(),t=Ni();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return Wt=r,r.default=r,e.registerRule(r),Wt}var qt,Cs;function ao(){if(Cs)return qt;Cs=1;let e=ft(),t=oo(),r=dt(),l=Tr(),s=ze(),f=Fr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},Ht=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),Ht}var Gt,Os;function lo(){if(Os)return Gt;Os=1;let e=Ai(),t=ct(),r=Ur();const l=Dr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return Gt=s,s.default=s,Gt}var Vt,Ms;function uo(){if(Ms)return Vt;Ms=1;let e=lo(),t=Pi(),r=Lr(),l=ze();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return Vt=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),Vt}var Jt,Es;function co(){if(Es)return Jt;Es=1;let e=ft(),t=Ii(),r=dt(),l=Tr(),s=pt(),f=ze(),u=Fr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Jt=m,m.default=m,Jt}var Yt,Is;function ho(){if(Is)return Yt;Is=1;let e=Pr(),t=ft(),r=Pi(),l=xe(),s=uo(),f=ct(),u=co(),m=Lr(),a=ki(),p=dt(),i=Tr(),c=Dr(),o=pt(),n=Ur(),d=Ni(),h=Fr(),y=ze(),C=ht();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),Yt=w,w.default=w,Yt}var fo=ho();const q=eo(fo);q.stringify;q.fromJSON;q.plugin;q.parse;q.list;q.document;q.comment;q.atRule;q.rule;q.decl;q.root;q.CssSyntaxError;q.Declaration;q.Container;q.Processor;q.Document;q.Comment;q.Warning;q.AtRule;q.Result;q.Input;q.Rule;q.Root;q.Node;var po=Object.defineProperty,mo=(e,t,r)=>t in e?po(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,oe=(e,t,r)=>mo(e,typeof t!="symbol"?t+"":t,r);function go(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function yo(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Je={exports:{}},As;function wo(){if(As)return Je.exports;As=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Je.exports=t(),Je.exports.createColors=t,Je.exports}const bo={},So=Object.freeze(Object.defineProperty({__proto__:null,default:bo},Symbol.toStringTag,{value:"Module"})),fe=yo(So);var Qt,ks;function $r(){if(ks)return Qt;ks=1;let e=wo(),t=fe;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Qt=r,r.default=r,Qt}var Ye={},Ns;function Br(){return Ns||(Ns=1,Ye.isClean=Symbol("isClean"),Ye.my=Symbol("my")),Ye}var Xt,Ps;function _i(){if(Ps)return Xt;Ps=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Xt=r,r.default=r,Xt}var Kt,_s;function mt(){if(_s)return Kt;_s=1;let e=_i();function t(r,l){new e(l).stringify(r)}return Kt=t,t.default=t,Kt}var Zt,Ls;function gt(){if(Ls)return Zt;Ls=1;let{isClean:e,my:t}=Br(),r=$r(),l=_i(),s=mt();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Zt=u,u.default=u,Zt}var er,Ds;function yt(){if(Ds)return er;Ds=1;let e=gt();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return er=t,t.default=t,er}var tr,Ts;function vo(){if(Ts)return tr;Ts=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return tr={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},tr}var rr,Fs;function Li(){if(Fs)return rr;Fs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{existsSync:r,readFileSync:l}=fe,{dirname:s,join:f}=fe;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return rr=m,m.default=m,rr}var sr,Us;function wt(){if(Us)return sr;Us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{fileURLToPath:r,pathToFileURL:l}=fe,{isAbsolute:s,resolve:f}=fe,{nanoid:u}=vo(),m=fe,a=$r(),p=Li(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return sr=n,n.default=n,m&&m.registerInput&&m.registerInput(n),sr}var ir,$s;function Di(){if($s)return ir;$s=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{dirname:r,relative:l,resolve:s,sep:f}=fe,{pathToFileURL:u}=fe,m=wt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return ir=i,ir}var nr,Bs;function bt(){if(Bs)return nr;Bs=1;let e=gt();class t extends e{constructor(l){super(l),this.type="comment"}}return nr=t,t.default=t,nr}var or,zs;function Re(){if(zs)return or;zs=1;let{isClean:e,my:t}=Br(),r=yt(),l=bt(),s=gt(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},or=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},or}var ar,Ws;function zr(){if(Ws)return ar;Ws=1;let e=Re(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},ar=l,l.default=l,ar}var lr,qs;function Ti(){if(qs)return lr;qs=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return lr=e,e.default=e,lr}var ur,js;function Wr(){if(js)return ur;js=1;let e=Ti();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return ur=t,t.default=t,ur}var cr,Hs;function Co(){if(Hs)return cr;Hs=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return cr=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},cr}var hr,Gs;function qr(){if(Gs)return hr;Gs=1;let e=Re();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return hr=t,t.default=t,e.registerAtRule(t),hr}var fr,Vs;function We(){if(Vs)return fr;Vs=1;let e=Re(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},fr=l,l.default=l,e.registerRoot(l),fr}var pr,Js;function Fi(){if(Js)return pr;Js=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return pr=e,e.default=e,pr}var dr,Ys;function jr(){if(Ys)return dr;Ys=1;let e=Re(),t=Fi();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return dr=r,r.default=r,e.registerRule(r),dr}var mr,Qs;function xo(){if(Qs)return mr;Qs=1;let e=yt(),t=Co(),r=bt(),l=qr(),s=We(),f=jr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},yr=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),yr}var wr,Zs;function Ro(){if(Zs)return wr;Zs=1;let e=Di(),t=mt(),r=Hr();const l=Wr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return wr=s,s.default=s,wr}var br,ei;function Oo(){if(ei)return br;ei=1;let e=Ro(),t=Ui(),r=zr(),l=We();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return br=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),br}var Sr,ti;function Mo(){if(ti)return Sr;ti=1;let e=yt(),t=Li(),r=bt(),l=qr(),s=wt(),f=We(),u=jr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Sr=m,m.default=m,Sr}var vr,ri;function Eo(){if(ri)return vr;ri=1;let e=$r(),t=yt(),r=Ui(),l=Re(),s=Oo(),f=mt(),u=Mo(),m=zr(),a=Ti(),p=bt(),i=qr(),c=Wr(),o=wt(),n=Hr(),d=Fi(),h=jr(),y=We(),C=gt();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),vr=w,w.default=w,vr}var Io=Eo();const j=go(Io);j.stringify;j.fromJSON;j.plugin;j.parse;j.list;j.document;j.comment;j.atRule;j.rule;j.decl;j.root;j.CssSyntaxError;j.Declaration;j.Container;j.Processor;j.Document;j.Comment;j.Warning;j.AtRule;j.Result;j.Input;j.Rule;j.Root;j.Node;class Gr{constructor(...t){oe(this,"parentElement",null),oe(this,"parentNode",null),oe(this,"ownerDocument"),oe(this,"firstChild",null),oe(this,"lastChild",null),oe(this,"previousSibling",null),oe(this,"nextSibling",null),oe(this,"ELEMENT_NODE",1),oe(this,"TEXT_NODE",3),oe(this,"nodeType"),oe(this,"nodeName"),oe(this,"RRNodeType")}get childNodes(){const t=[];let r=this.firstChild;for(;r;)t.push(r),r=r.nextSibling;return t}contains(t){if(t instanceof Gr){if(t.ownerDocument!==this.ownerDocument)return!1;if(t===this)return!0}else return!1;for(;t.parentNode;){if(t.parentNode===this)return!0;t=t.parentNode}return!1}appendChild(t){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(t,r){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(t){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const si={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ii={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},Qe={};function Ao(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function Vr(e){if(Qe[e])return Qe[e];const t=Ao(e)||globalThis[e],r=t.prototype,l=e in si?si[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in ii?ii[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return Qe[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?Qe[e]=p:r}catch{return r}}const Cr={};function Se(e,t,r){var l;const s=`${e}.${String(r)}`;if(Cr[s])return Cr[s].call(t);const f=Vr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Cr[s]=u,u.call(t)):t[r]}const xr={};function $i(e,t,r){const l=`${e}.${String(r)}`;if(xr[l])return xr[l].bind(t);const f=Vr(e)[r];return typeof f!="function"?t[r]:(xr[l]=f,f.bind(t))}function ko(e){return Se("Node",e,"childNodes")}function No(e){return Se("Node",e,"parentNode")}function Po(e){return Se("Node",e,"parentElement")}function _o(e){return Se("Node",e,"textContent")}function Lo(e,t){return $i("Node",e,"contains")(t)}function Do(e){return $i("Node",e,"getRootNode")()}function To(e){return!e||!("host"in e)?null:Se("ShadowRoot",e,"host")}function Fo(e){return e.styleSheets}function Uo(e){return!e||!("shadowRoot"in e)?null:Se("Element",e,"shadowRoot")}function $o(e,t){return Se("Element",e,"querySelector")(t)}function Bo(e,t){return Se("Element",e,"querySelectorAll")(t)}function Bi(){return Vr("MutationObserver").constructor}const L={childNodes:ko,parentNode:No,parentElement:Po,textContent:_o,contains:Lo,getRootNode:Do,host:To,styleSheets:Fo,shadowRoot:Uo,querySelector:$o,querySelectorAll:Bo,mutationObserver:Bi};function te(e,t,r=document){const l={capture:!0,passive:!0};return r.addEventListener(e,t,l),()=>r.removeEventListener(e,t,l)}const Ie=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let ni={map:{},getId(){return console.error(Ie),-1},getNode(){return console.error(Ie),null},removeNodeFromMap(){console.error(Ie)},has(){return console.error(Ie),!1},reset(){console.error(Ie)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(ni=new Proxy(ni,{get(e,t,r){return t==="map"&&console.error(Ie),Reflect.get(e,t,r)}}));function Be(e,t,r={}){let l=null,s=0;return function(...f){const u=Date.now();!s&&r.leading===!1&&(s=u);const m=t-(u-s),a=this;m<=0||m>t?(l&&(clearTimeout(l),l=null),s=u,e.apply(a,f)):!l&&r.trailing!==!1&&(l=setTimeout(()=>{s=r.leading===!1?0:Date.now(),l=null,e.apply(a,f)},m))}}function St(e,t,r,l,s=window){const f=s.Object.getOwnPropertyDescriptor(e,t);return s.Object.defineProperty(e,t,l?r:{set(u){setTimeout(()=>{r.set.call(this,u)},0),f&&f.set&&f.set.call(this,u)}}),()=>St(e,t,f||{},!0)}function Le(e,t,r){try{if(!(t in e))return()=>{};const l=e[t],s=r(l);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:l}})),e[t]=s,()=>{e[t]=l}}catch{return()=>{}}}let at=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(at=()=>new Date().getTime());function zi(e){var t,r,l,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((t=L.parentElement(f.body))==null?void 0:t.scrollLeft)||((r=f?.body)==null?void 0:r.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((l=L.parentElement(f.body))==null?void 0:l.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Wi(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function qi(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function ji(e){return e?e.nodeType===e.ELEMENT_NODE?e:L.parentElement(e):null}function re(e,t,r,l){if(!e)return!1;const s=ji(e);if(!s)return!1;try{if(typeof t=="string"){if(s.classList.contains(t)||l&&s.closest("."+t)!==null)return!0}else if(ot(s,t,l))return!0}catch{}return!!(r&&(s.matches(r)||l&&s.closest(r)!==null))}function zo(e,t){return t.getId(e)!==-1}function Rr(e,t,r){return e.tagName==="TITLE"&&r.headTitleMutations?!0:t.getId(e)===$e}function Hi(e,t){if(Te(e))return!1;const r=t.getId(e);if(!t.has(r))return!0;const l=L.parentNode(e);return l&&l.nodeType===e.DOCUMENT_NODE?!1:l?Hi(l,t):!0}function Er(e){return!!e.changedTouches}function Wo(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function Gi(e,t){return!!(e.nodeName==="IFRAME"&&t.getMeta(e))}function Vi(e,t){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&t.getMeta(e))}function Ir(e){return e?e instanceof Gr&&"shadowRoot"in e?!!e.shadowRoot:!!L.shadowRoot(e):!1}class qo{constructor(){I(this,"id",1),I(this,"styleIDMap",new WeakMap),I(this,"idStyleMap",new Map)}getId(t){return this.styleIDMap.get(t)??-1}has(t){return this.styleIDMap.has(t)}add(t,r){if(this.has(t))return this.getId(t);let l;return r===void 0?l=this.id++:l=r,this.styleIDMap.set(t,l),this.idStyleMap.set(l,t),l}getStyle(t){return this.idStyleMap.get(t)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function Ji(e){var t;let r=null;return"getRootNode"in e&&((t=L.getRootNode(e))==null?void 0:t.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&L.host(L.getRootNode(e))&&(r=L.host(L.getRootNode(e))),r}function jo(e){let t=e,r;for(;r=Ji(t);)t=r;return t}function Ho(e){const t=e.ownerDocument;if(!t)return!1;const r=jo(e);return L.contains(t,r)}function Yi(e){const t=e.ownerDocument;return t?L.contains(t,e)||Ho(e):!1}var U=(e=>(e[e.DomContentLoaded=0]="DomContentLoaded",e[e.Load=1]="Load",e[e.FullSnapshot=2]="FullSnapshot",e[e.IncrementalSnapshot=3]="IncrementalSnapshot",e[e.Meta=4]="Meta",e[e.Custom=5]="Custom",e[e.Plugin=6]="Plugin",e))(U||{}),D=(e=>(e[e.Mutation=0]="Mutation",e[e.MouseMove=1]="MouseMove",e[e.MouseInteraction=2]="MouseInteraction",e[e.Scroll=3]="Scroll",e[e.ViewportResize=4]="ViewportResize",e[e.Input=5]="Input",e[e.TouchMove=6]="TouchMove",e[e.MediaInteraction=7]="MediaInteraction",e[e.StyleSheetRule=8]="StyleSheetRule",e[e.CanvasMutation=9]="CanvasMutation",e[e.Font=10]="Font",e[e.Log=11]="Log",e[e.Drag=12]="Drag",e[e.StyleDeclaration=13]="StyleDeclaration",e[e.Selection=14]="Selection",e[e.AdoptedStyleSheet=15]="AdoptedStyleSheet",e[e.CustomElement=16]="CustomElement",e))(D||{}),ie=(e=>(e[e.MouseUp=0]="MouseUp",e[e.MouseDown=1]="MouseDown",e[e.Click=2]="Click",e[e.ContextMenu=3]="ContextMenu",e[e.DblClick=4]="DblClick",e[e.Focus=5]="Focus",e[e.Blur=6]="Blur",e[e.TouchStart=7]="TouchStart",e[e.TouchMove_Departed=8]="TouchMove_Departed",e[e.TouchEnd=9]="TouchEnd",e[e.TouchCancel=10]="TouchCancel",e))(ie||{}),ge=(e=>(e[e.Mouse=0]="Mouse",e[e.Pen=1]="Pen",e[e.Touch=2]="Touch",e))(ge||{}),_e=(e=>(e[e["2D"]=0]="2D",e[e.WebGL=1]="WebGL",e[e.WebGL2=2]="WebGL2",e))(_e||{}),Ae=(e=>(e[e.Play=0]="Play",e[e.Pause=1]="Pause",e[e.Seeked=2]="Seeked",e[e.VolumeChange=3]="VolumeChange",e[e.RateChange=4]="RateChange",e))(Ae||{}),Qi=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Qi||{});function oi(e){return"__ln"in e}class Go{constructor(){I(this,"length",0),I(this,"head",null),I(this,"tail",null)}get(t){if(t>=this.length)throw new Error("Position outside of list range");let r=this.head;for(let l=0;l`${e}@${t}`;class Vo{constructor(){I(this,"frozen",!1),I(this,"locked",!1),I(this,"texts",[]),I(this,"attributes",[]),I(this,"attributeMap",new WeakMap),I(this,"removes",[]),I(this,"mapRemoves",[]),I(this,"movedMap",{}),I(this,"addedSet",new Set),I(this,"movedSet",new Set),I(this,"droppedSet",new Set),I(this,"removesSubTreeCache",new Set),I(this,"mutationCb"),I(this,"blockClass"),I(this,"blockSelector"),I(this,"maskTextClass"),I(this,"maskTextSelector"),I(this,"inlineStylesheet"),I(this,"maskInputOptions"),I(this,"maskTextFn"),I(this,"maskInputFn"),I(this,"maskAttributeFn"),I(this,"keepIframeSrcFn"),I(this,"recordCanvas"),I(this,"inlineImages"),I(this,"slimDOMOptions"),I(this,"dataURLOptions"),I(this,"doc"),I(this,"mirror"),I(this,"iframeManager"),I(this,"stylesheetManager"),I(this,"shadowDomManager"),I(this,"canvasManager"),I(this,"processedNodeManager"),I(this,"unattachedDoc"),I(this,"processMutations",t=>{t.forEach(this.processMutation),this.emit()}),I(this,"emit",()=>{if(this.frozen||this.locked)return;const t=[],r=new Set,l=new Go,s=a=>{let p=a,i=$e;for(;i===$e;)p=p&&p.nextSibling,i=p&&this.mirror.getId(p);return i},f=a=>{const p=L.parentNode(a);if(!p||!Yi(a))return;let i=!1;if(a.nodeType===Node.TEXT_NODE){const d=p.tagName;if(d==="TEXTAREA")return;d==="STYLE"&&this.addedSet.has(p)&&(i=!0)}const c=Te(p)?this.mirror.getId(Ji(a)):this.mirror.getId(p),o=s(a);if(c===-1||o===-1)return l.addNode(a);const n=Ne(a,{doc:this.doc,mirror:this.mirror,blockClass:this.blockClass,blockSelector:this.blockSelector,maskTextClass:this.maskTextClass,maskTextSelector:this.maskTextSelector,skipChild:!0,newlyAddedElement:!0,inlineStylesheet:this.inlineStylesheet,maskInputOptions:this.maskInputOptions,maskTextFn:this.maskTextFn,maskInputFn:this.maskInputFn,slimDOMOptions:this.slimDOMOptions,dataURLOptions:this.dataURLOptions,recordCanvas:this.recordCanvas,inlineImages:this.inlineImages,onSerialize:d=>{Gi(d,this.mirror)&&this.iframeManager.addIframe(d),Vi(d,this.mirror)&&this.stylesheetManager.trackLinkElement(d),Ir(a)&&this.shadowDomManager.addShadowRoot(L.shadowRoot(a),this.doc)},onIframeLoad:(d,h)=>{this.iframeManager.attachIframe(d,h),this.shadowDomManager.observeAttachShadow(d)},onStylesheetLoad:(d,h)=>{this.stylesheetManager.attachLinkElement(d,h)},cssCaptured:i});n&&(t.push({parentId:c,nextId:o,node:n}),r.add(n.id))};for(;this.mapRemoves.length;)this.mirror.removeNodeFromMap(this.mapRemoves.shift());for(const a of this.movedSet)li(this.removesSubTreeCache,a,this.mirror)&&!this.movedSet.has(L.parentNode(a))||f(a);for(const a of this.addedSet)!ui(this.droppedSet,a)&&!li(this.removesSubTreeCache,a,this.mirror)||ui(this.movedSet,a)?f(a):this.droppedSet.add(a);let u=null;for(;l.length;){let a=null;if(u){const p=this.mirror.getId(L.parentNode(u.value)),i=s(u.value);p!==-1&&i!==-1&&(a=u)}if(!a){let p=l.tail;for(;p;){const i=p;if(p=p.previous,i){const c=this.mirror.getId(L.parentNode(i.value));if(s(i.value)===-1)continue;if(c!==-1){a=i;break}else{const n=i.value,d=L.parentNode(n);if(d&&d.nodeType===Node.DOCUMENT_FRAGMENT_NODE){const h=L.host(d);if(this.mirror.getId(h)!==-1){a=i;break}}}}}}if(!a){for(;l.head;)l.removeNode(l.head.value);break}u=a.previous,l.removeNode(a.value),f(a.value)}const m={texts:this.texts.map(a=>{const p=a.node,i=L.parentNode(p);return i&&i.tagName==="TEXTAREA"&&this.genTextAreaValueMutation(i),{id:this.mirror.getId(p),value:a.value}}).filter(a=>!r.has(a.id)).filter(a=>this.mirror.has(a.id)),attributes:this.attributes.map(a=>{const{attributes:p}=a;if(typeof p.style=="string"){const i=JSON.stringify(a.styleDiff),c=JSON.stringify(a._unchangedStyles);i.length!r.has(a.id)).filter(a=>this.mirror.has(a.id)),removes:this.removes,adds:t};!m.texts.length&&!m.attributes.length&&!m.removes.length&&!m.adds.length||(this.texts=[],this.attributes=[],this.attributeMap=new WeakMap,this.removes=[],this.addedSet=new Set,this.movedSet=new Set,this.droppedSet=new Set,this.removesSubTreeCache=new Set,this.movedMap={},this.mutationCb(m))}),I(this,"genTextAreaValueMutation",t=>{let r=this.attributeMap.get(t);r||(r={node:t,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(r),this.attributeMap.set(t,r));const l=Array.from(L.childNodes(t),s=>L.textContent(s)||"").join("");r.attributes.value=st({element:t,maskInputOptions:this.maskInputOptions,tagName:t.tagName,type:it(t),value:l,maskInputFn:this.maskInputFn})}),I(this,"processMutation",t=>{if(!Rr(t.target,this.mirror,this.slimDOMOptions))switch(t.type){case"characterData":{const r=L.textContent(t.target);!re(t.target,this.blockClass,this.blockSelector,!1)&&r!==t.oldValue&&this.texts.push({value:Mi(t.target,this.maskTextClass,this.maskTextSelector,!0)&&r?this.maskTextFn?this.maskTextFn(r,ji(t.target)):r.replace(/[\S]/g,"*"):r,node:t.target});break}case"attributes":{const r=t.target;let l=t.attributeName,s=t.target.getAttribute(l);if(l==="value"){const u=it(r);s=st({element:r,maskInputOptions:this.maskInputOptions,tagName:r.tagName,type:u,value:s,maskInputFn:this.maskInputFn})}if(re(t.target,this.blockClass,this.blockSelector,!1)||s===t.oldValue)return;let f=this.attributeMap.get(t.target);if(r.tagName==="IFRAME"&&l==="src"&&!this.keepIframeSrcFn(s))if(!r.contentDocument)l="rr_src";else return;if(f||(f={node:t.target,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(f),this.attributeMap.set(t.target,f)),l==="type"&&r.tagName==="INPUT"&&(t.oldValue||"").toLowerCase()==="password"&&r.setAttribute("data-rr-is-password","true"),!Oi(r.tagName,l)){if(f.attributes[l]=Ri(this.doc,Ce(r.tagName),Ce(l),s),this.maskAttributeFn&&typeof f.attributes[l]=="string"){const u=this.maskAttributeFn(l,f.attributes[l],r);u!==null?f.attributes[l]=u:delete f.attributes[l]}if(l==="style"){if(!this.unattachedDoc)try{this.unattachedDoc=document.implementation.createHTMLDocument()}catch{this.unattachedDoc=this.doc}const u=this.unattachedDoc.createElement("span");t.oldValue&&u.setAttribute("style",t.oldValue);for(const m of Array.from(r.style)){const a=r.style.getPropertyValue(m),p=r.style.getPropertyPriority(m);a!==u.style.getPropertyValue(m)||p!==u.style.getPropertyPriority(m)?p===""?f.styleDiff[m]=a:f.styleDiff[m]=[a,p]:f._unchangedStyles[m]=[a,p]}for(const m of Array.from(u.style))r.style.getPropertyValue(m)===""&&(f.styleDiff[m]=!1)}else l==="open"&&r.tagName==="DIALOG"&&(r.matches("dialog:modal")?f.attributes.rr_open_mode="modal":f.attributes.rr_open_mode="non-modal")}break}case"childList":{if(re(t.target,this.blockClass,this.blockSelector,!0))return;if(t.target.tagName==="TEXTAREA"){this.genTextAreaValueMutation(t.target);return}t.addedNodes.forEach(r=>this.genAdds(r,t.target)),t.removedNodes.forEach(r=>{const l=this.mirror.getId(r),s=Te(t.target)?this.mirror.getId(L.host(t.target)):this.mirror.getId(t.target);re(t.target,this.blockClass,this.blockSelector,!1)||Rr(r,this.mirror,this.slimDOMOptions)||!zo(r,this.mirror)||(this.addedSet.has(r)?(Ar(this.addedSet,r),this.droppedSet.add(r)):this.addedSet.has(t.target)&&l===-1||Hi(t.target,this.mirror)||(this.movedSet.has(r)&&this.movedMap[ai(l,s)]?Ar(this.movedSet,r):(this.removes.push({parentId:s,id:l,isShadow:Te(t.target)&&Fe(t.target)?!0:void 0}),Jo(r,this.removesSubTreeCache))),this.mapRemoves.push(r))});break}}}),I(this,"genAdds",(t,r)=>{if(!this.processedNodeManager.inOtherBuffer(t,this)&&!(this.addedSet.has(t)||this.movedSet.has(t))){if(this.mirror.hasNode(t)){if(Rr(t,this.mirror,this.slimDOMOptions))return;this.movedSet.add(t);let l=null;r&&this.mirror.hasNode(r)&&(l=this.mirror.getId(r)),l&&l!==-1&&(this.movedMap[ai(this.mirror.getId(t),l)]=!0)}else this.addedSet.add(t),this.droppedSet.delete(t);re(t,this.blockClass,this.blockSelector,!1)||(L.childNodes(t).forEach(l=>this.genAdds(l)),Ir(t)&&L.childNodes(L.shadowRoot(t)).forEach(l=>{this.processedNodeManager.add(l,this),this.genAdds(l,t)}))}})}init(t){["mutationCb","blockClass","blockSelector","maskTextClass","maskTextSelector","inlineStylesheet","maskInputOptions","maskTextFn","maskInputFn","maskAttributeFn","keepIframeSrcFn","recordCanvas","inlineImages","slimDOMOptions","dataURLOptions","doc","mirror","iframeManager","stylesheetManager","shadowDomManager","canvasManager","processedNodeManager"].forEach(r=>{this[r]=t[r]})}freeze(){this.frozen=!0,this.canvasManager.freeze()}unfreeze(){this.frozen=!1,this.canvasManager.unfreeze(),this.emit()}isFrozen(){return this.frozen}lock(){this.locked=!0,this.canvasManager.lock()}unlock(){this.locked=!1,this.canvasManager.unlock(),this.emit()}reset(){this.shadowDomManager.reset(),this.canvasManager.reset()}}function Ar(e,t){e.delete(t),L.childNodes(t).forEach(r=>Ar(e,r))}function Jo(e,t){const r=[e];for(;r.length;){const l=r.pop();t.has(l)||(t.add(l),L.childNodes(l).forEach(s=>r.push(s)))}}function li(e,t,r){return e.size===0?!1:Yo(e,t)}function Yo(e,t,r){const l=L.parentNode(t);return l?e.has(l):!1}function ui(e,t){return e.size===0?!1:Xi(e,t)}function Xi(e,t){const r=L.parentNode(t);return r?e.has(r)?!0:Xi(e,r):!1}let Ue;function Qo(e){Ue=e}function Xo(){Ue=void 0}const T=e=>Ue?((...r)=>{try{return e(...r)}catch(l){if(Ue&&Ue(l)===!0)return;throw l}}):e;function ci(e){return(...t)=>{try{return e(...t)}catch(r){try{r._external_=!0}catch{}throw r}}}const ve=[];function qe(e){try{if("composedPath"in e){const t=e.composedPath();if(t.length)return t[0]}else if("path"in e&&e.path.length)return e.path[0]}catch{}return e&&e.target}function Ki(e,t){const r=new Vo;ve.push(r),r.init(e);const l=new(Bi())(T(r.processMutations.bind(r)));return l.observe(t,{attributes:!0,attributeOldValue:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0}),l}function Ko({mousemoveCb:e,sampling:t,doc:r,mirror:l}){if(t.mousemove===!1)return()=>{};const s=typeof t.mousemove=="number"?t.mousemove:50,f=typeof t.mousemoveCallback=="number"?t.mousemoveCallback:500;let u=[],m;const a=Be(T(c=>{const o=Date.now()-m;e(u.map(n=>(n.timeOffset-=o,n)),c),u=[],m=null}),f),p=T(Be(T(c=>{const o=qe(c),{clientX:n,clientY:d}=Er(c)?c.changedTouches[0]:c;m||(m=at()),u.push({x:n,y:d,id:l.getId(o),timeOffset:at()-m}),a(typeof DragEvent<"u"&&c instanceof DragEvent?D.Drag:c instanceof MouseEvent?D.MouseMove:D.TouchMove)}),s,{trailing:!1})),i=[te("mousemove",p,r),te("touchmove",p,r),te("drag",p,r)];return T(()=>{i.forEach(c=>c())})}function Zo({mouseInteractionCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){if(f.mouseInteraction===!1)return()=>{};const u=f.mouseInteraction===!0||f.mouseInteraction===void 0?{}:f.mouseInteraction,m=[];let a=null;const p=i=>c=>{const o=qe(c);if(re(o,l,s,!0))return;let n=null,d=i;if("pointerType"in c){switch(c.pointerType){case"mouse":n=ge.Mouse;break;case"touch":n=ge.Touch;break;case"pen":n=ge.Pen;break}n===ge.Touch?ie[i]===ie.MouseDown?d="TouchStart":ie[i]===ie.MouseUp&&(d="TouchEnd"):ge.Pen}else Er(c)&&(n=ge.Touch);n!==null?(a=n,(d.startsWith("Touch")&&n===ge.Touch||d.startsWith("Mouse")&&n===ge.Mouse)&&(n=null)):ie[i]===ie.Click&&(n=a,a=null);const h=Er(c)?c.changedTouches[0]:c;if(!h)return;const y=r.getId(o),{clientX:C,clientY:w}=h;T(e)({type:ie[d],id:y,x:C,y:w,...n!==null&&{pointerType:n}})};return Object.keys(ie).filter(i=>Number.isNaN(Number(i))&&!i.endsWith("_Departed")&&u[i]!==!1).forEach(i=>{let c=Ce(i);const o=p(i);if(window.PointerEvent)switch(ie[i]){case ie.MouseDown:case ie.MouseUp:c=c.replace("mouse","pointer");break;case ie.TouchStart:case ie.TouchEnd:return}m.push(te(c,o,t))}),T(()=>{m.forEach(i=>i())})}function Zi({scrollCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){const u=T(Be(T(m=>{const a=qe(m);if(!a||re(a,l,s,!0))return;const p=r.getId(a);if(a===t&&t.defaultView){const i=zi(t.defaultView);e({id:p,x:i.left,y:i.top})}else e({id:p,x:a.scrollLeft,y:a.scrollTop})}),f.scroll||100));return te("scroll",u,t)}function ea({viewportResizeCb:e},{win:t}){let r=-1,l=-1;const s=T(Be(T(()=>{const f=Wi(),u=qi();(r!==f||l!==u)&&(e({width:Number(u),height:Number(f)}),r=f,l=u)}),200));return te("resize",s,t)}const ta=["INPUT","TEXTAREA","SELECT"],hi=new WeakMap;function ra({inputCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,ignoreClass:f,ignoreSelector:u,maskInputOptions:m,maskInputFn:a,sampling:p,userTriggeredOnInput:i}){function c(w){let S=qe(w);const b=w.isTrusted,g=S&&S.tagName;if(S&&g==="OPTION"&&(S=L.parentElement(S)),!S||!g||ta.indexOf(g)<0||re(S,l,s,!0)||S.classList.contains(f)||u&&S.matches(u))return;let v=S.value,x=!1;const O=it(S)||"";O==="radio"||O==="checkbox"?x=S.checked:(m[g.toLowerCase()]||m[O])&&(v=st({element:S,maskInputOptions:m,tagName:g,type:O,value:v,maskInputFn:a})),o(S,i?{text:v,isChecked:x,userTriggered:b}:{text:v,isChecked:x});const A=S.name;O==="radio"&&A&&x&&t.querySelectorAll(`input[type="radio"][name="${A}"]`).forEach(M=>{if(M!==S){const $=M.value;o(M,i?{text:$,isChecked:!x,userTriggered:!1}:{text:$,isChecked:!x})}})}function o(w,S){const b=hi.get(w);if(!b||b.text!==S.text||b.isChecked!==S.isChecked){hi.set(w,S);const g=r.getId(w);T(e)({...S,id:g})}}const d=(p.input==="last"?["change"]:["input","change"]).map(w=>te(w,T(c),t)),h=t.defaultView;if(!h)return()=>{d.forEach(w=>w())};const y=h.Object.getOwnPropertyDescriptor(h.HTMLInputElement.prototype,"value"),C=[[h.HTMLInputElement.prototype,"value"],[h.HTMLInputElement.prototype,"checked"],[h.HTMLSelectElement.prototype,"value"],[h.HTMLTextAreaElement.prototype,"value"],[h.HTMLSelectElement.prototype,"selectedIndex"],[h.HTMLOptionElement.prototype,"selected"]];return y&&y.set&&d.push(...C.map(w=>St(w[0],w[1],{set(){T(c)({target:this,isTrusted:!1})}},!1,h))),T(()=>{d.forEach(w=>w())})}function lt(e){const t=[];function r(l,s){if(Xe("CSSGroupingRule")&&l.parentRule instanceof CSSGroupingRule||Xe("CSSMediaRule")&&l.parentRule instanceof CSSMediaRule||Xe("CSSSupportsRule")&&l.parentRule instanceof CSSSupportsRule||Xe("CSSConditionRule")&&l.parentRule instanceof CSSConditionRule){const u=Array.from(l.parentRule.cssRules).indexOf(l);s.unshift(u)}else if(l.parentStyleSheet){const u=Array.from(l.parentStyleSheet.cssRules).indexOf(l);s.unshift(u)}return s}return r(e,t)}function we(e,t,r){let l,s;return e?(e.ownerNode?l=t.getId(e.ownerNode):s=r.getId(e),{styleId:s,id:l}):{}}function sa({styleSheetRuleCb:e,mirror:t,stylesheetManager:r},{win:l}){if(!l.CSSStyleSheet||!l.CSSStyleSheet.prototype)return()=>{};const s=l.CSSStyleSheet.prototype.insertRule;l.CSSStyleSheet.prototype.insertRule=new Proxy(s,{apply:T((i,c,o)=>{const[n,d]=o,{id:h,styleId:y}=we(c,t,r.styleMirror);return(h&&h!==-1||y&&y!==-1)&&e({id:h,styleId:y,adds:[{rule:n,index:d}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.addRule=function(i,c,o=this.cssRules.length){const n=`${i} { ${c} }`;return l.CSSStyleSheet.prototype.insertRule.apply(this,[n,o])};const f=l.CSSStyleSheet.prototype.deleteRule;l.CSSStyleSheet.prototype.deleteRule=new Proxy(f,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,removes:[{index:n}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.removeRule=function(i){return l.CSSStyleSheet.prototype.deleteRule.apply(this,[i])};let u;l.CSSStyleSheet.prototype.replace&&(u=l.CSSStyleSheet.prototype.replace,l.CSSStyleSheet.prototype.replace=new Proxy(u,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replace:n}),i.apply(c,o)})}));let m;l.CSSStyleSheet.prototype.replaceSync&&(m=l.CSSStyleSheet.prototype.replaceSync,l.CSSStyleSheet.prototype.replaceSync=new Proxy(m,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replaceSync:n}),i.apply(c,o)})}));const a={};Ke("CSSGroupingRule")?a.CSSGroupingRule=l.CSSGroupingRule:(Ke("CSSMediaRule")&&(a.CSSMediaRule=l.CSSMediaRule),Ke("CSSConditionRule")&&(a.CSSConditionRule=l.CSSConditionRule),Ke("CSSSupportsRule")&&(a.CSSSupportsRule=l.CSSSupportsRule));const p={};return Object.entries(a).forEach(([i,c])=>{p[i]={insertRule:c.prototype.insertRule,deleteRule:c.prototype.deleteRule},c.prototype.insertRule=new Proxy(p[i].insertRule,{apply:T((o,n,d)=>{const[h,y]=d,{id:C,styleId:w}=we(n.parentStyleSheet,t,r.styleMirror);return(C&&C!==-1||w&&w!==-1)&&e({id:C,styleId:w,adds:[{rule:h,index:[...lt(n),y||0]}]}),o.apply(n,d)})}),c.prototype.deleteRule=new Proxy(p[i].deleteRule,{apply:T((o,n,d)=>{const[h]=d,{id:y,styleId:C}=we(n.parentStyleSheet,t,r.styleMirror);return(y&&y!==-1||C&&C!==-1)&&e({id:y,styleId:C,removes:[{index:[...lt(n),h]}]}),o.apply(n,d)})})}),T(()=>{l.CSSStyleSheet.prototype.insertRule=s,l.CSSStyleSheet.prototype.deleteRule=f,u&&(l.CSSStyleSheet.prototype.replace=u),m&&(l.CSSStyleSheet.prototype.replaceSync=m),Object.entries(a).forEach(([i,c])=>{c.prototype.insertRule=p[i].insertRule,c.prototype.deleteRule=p[i].deleteRule})})}function en({mirror:e,stylesheetManager:t},r){var l,s,f;let u=null;r.nodeName==="#document"?u=e.getId(r):u=e.getId(L.host(r));const m=r.nodeName==="#document"?(l=r.defaultView)==null?void 0:l.Document:(f=(s=r.ownerDocument)==null?void 0:s.defaultView)==null?void 0:f.ShadowRoot,a=m?.prototype?Object.getOwnPropertyDescriptor(m?.prototype,"adoptedStyleSheets"):void 0;return u===null||u===-1||!m||!a?()=>{}:(Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get(){var p;return(p=a.get)==null?void 0:p.call(this)},set(p){var i;const c=(i=a.set)==null?void 0:i.call(this,p);if(u!==null&&u!==-1)try{t.adoptStyleSheets(p,u)}catch{}return c}}),T(()=>{Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get:a.get,set:a.set})}))}function ia({styleDeclarationCb:e,mirror:t,ignoreCSSAttributes:r,stylesheetManager:l},{win:s}){const f=s.CSSStyleDeclaration.prototype.setProperty;s.CSSStyleDeclaration.prototype.setProperty=new Proxy(f,{apply:T((m,a,p)=>{var i;const[c,o,n]=p;if(r.has(c))return f.apply(a,[c,o,n]);const{id:d,styleId:h}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,set:{property:c,value:o,priority:n},index:lt(a.parentRule)}),m.apply(a,p)})});const u=s.CSSStyleDeclaration.prototype.removeProperty;return s.CSSStyleDeclaration.prototype.removeProperty=new Proxy(u,{apply:T((m,a,p)=>{var i;const[c]=p;if(r.has(c))return u.apply(a,[c]);const{id:o,styleId:n}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(o&&o!==-1||n&&n!==-1)&&e({id:o,styleId:n,remove:{property:c},index:lt(a.parentRule)}),m.apply(a,p)})}),T(()=>{s.CSSStyleDeclaration.prototype.setProperty=f,s.CSSStyleDeclaration.prototype.removeProperty=u})}function na({mediaInteractionCb:e,blockClass:t,blockSelector:r,mirror:l,sampling:s,doc:f}){const u=T(a=>Be(T(p=>{const i=qe(p);if(!i||re(i,t,r,!0))return;const{currentTime:c,volume:o,muted:n,playbackRate:d,loop:h}=i;e({type:a,id:l.getId(i),currentTime:c,volume:o,muted:n,playbackRate:d,loop:h})}),s.media||500)),m=[te("play",u(Ae.Play),f),te("pause",u(Ae.Pause),f),te("seeked",u(Ae.Seeked),f),te("volumechange",u(Ae.VolumeChange),f),te("ratechange",u(Ae.RateChange),f)];return T(()=>{m.forEach(a=>a())})}function oa({fontCb:e,doc:t}){const r=t.defaultView;if(!r)return()=>{};const l=[],s=new WeakMap,f=r.FontFace;r.FontFace=function(a,p,i){const c=new f(a,p,i);return s.set(c,{family:a,buffer:typeof p!="string",descriptors:i,fontSource:typeof p=="string"?p:JSON.stringify(Array.from(new Uint8Array(p)))}),c};const u=Le(t.fonts,"add",function(m){return function(a){return setTimeout(T(()=>{const p=s.get(a);p&&(e(p),s.delete(a))}),0),m.apply(this,[a])}});return l.push(()=>{r.FontFace=f}),l.push(u),T(()=>{l.forEach(m=>m())})}function aa(e){const{doc:t,mirror:r,blockClass:l,blockSelector:s,selectionCb:f}=e;let u=!0;const m=T(()=>{const a=t.getSelection();if(!a||u&&a?.isCollapsed)return;u=a.isCollapsed||!1;const p=[],i=a.rangeCount||0;for(let c=0;c{}:Le(r.customElements,"define",function(s){return function(f,u,m){try{t({define:{name:f}})}catch{console.warn(`Custom element callback failed for ${f}`)}return s.apply(this,[f,u,m])}})}function ua(e,t){const{mutationCb:r,mousemoveCb:l,mouseInteractionCb:s,scrollCb:f,viewportResizeCb:u,inputCb:m,mediaInteractionCb:a,styleSheetRuleCb:p,styleDeclarationCb:i,canvasMutationCb:c,fontCb:o,selectionCb:n,customElementCb:d}=e;e.mutationCb=(...h)=>{t.mutation&&t.mutation(...h),r(...h)},e.mousemoveCb=(...h)=>{t.mousemove&&t.mousemove(...h),l(...h)},e.mouseInteractionCb=(...h)=>{t.mouseInteraction&&t.mouseInteraction(...h),s(...h)},e.scrollCb=(...h)=>{t.scroll&&t.scroll(...h),f(...h)},e.viewportResizeCb=(...h)=>{t.viewportResize&&t.viewportResize(...h),u(...h)},e.inputCb=(...h)=>{t.input&&t.input(...h),m(...h)},e.mediaInteractionCb=(...h)=>{t.mediaInteaction&&t.mediaInteaction(...h),a(...h)},e.styleSheetRuleCb=(...h)=>{t.styleSheetRule&&t.styleSheetRule(...h),p(...h)},e.styleDeclarationCb=(...h)=>{t.styleDeclaration&&t.styleDeclaration(...h),i(...h)},e.canvasMutationCb=(...h)=>{t.canvasMutation&&t.canvasMutation(...h),c(...h)},e.fontCb=(...h)=>{t.font&&t.font(...h),o(...h)},e.selectionCb=(...h)=>{t.selection&&t.selection(...h),n(...h)},e.customElementCb=(...h)=>{t.customElement&&t.customElement(...h),d(...h)}}function ca(e,t={}){const r=e.doc.defaultView;if(!r)return()=>{};ua(e,t);let l;e.recordDOM&&(l=Ki(e,e.doc));const s=Ko(e),f=Zo(e),u=Zi(e),m=ea(e,{win:r}),a=ra(e),p=na(e);let i=()=>{},c=()=>{},o=()=>{},n=()=>{};e.recordDOM&&(i=sa(e,{win:r}),c=en(e,e.doc),o=ia(e,{win:r}),e.collectFonts&&(n=oa(e)));const d=aa(e),h=la(e),y=[];for(const C of e.plugins)y.push(C.observer(C.callback,r,C.options));return T(()=>{ve.forEach(C=>C.reset()),l?.disconnect(),s(),f(),u(),m(),a(),p(),i(),c(),o(),n(),d(),h(),y.forEach(C=>C())})}function Xe(e){return typeof window[e]<"u"}function Ke(e){return!!(typeof window[e]<"u"&&window[e].prototype&&"insertRule"in window[e].prototype&&"deleteRule"in window[e].prototype)}class fi{constructor(t){I(this,"iframeIdToRemoteIdMap",new WeakMap),I(this,"iframeRemoteIdToIdMap",new WeakMap),this.generateIdFn=t}getId(t,r,l,s){const f=l||this.getIdToRemoteIdMap(t),u=s||this.getRemoteIdToIdMap(t);let m=f.get(r);return m||(m=this.generateIdFn(),f.set(r,m),u.set(m,r)),m}getIds(t,r){const l=this.getIdToRemoteIdMap(t),s=this.getRemoteIdToIdMap(t);return r.map(f=>this.getId(t,f,l,s))}getRemoteId(t,r,l){const s=l||this.getRemoteIdToIdMap(t);if(typeof r!="number")return r;const f=s.get(r);return f||-1}getRemoteIds(t,r){const l=this.getRemoteIdToIdMap(t);return r.map(s=>this.getRemoteId(t,s,l))}reset(t){if(!t){this.iframeIdToRemoteIdMap=new WeakMap,this.iframeRemoteIdToIdMap=new WeakMap;return}this.iframeIdToRemoteIdMap.delete(t),this.iframeRemoteIdToIdMap.delete(t)}getIdToRemoteIdMap(t){let r=this.iframeIdToRemoteIdMap.get(t);return r||(r=new Map,this.iframeIdToRemoteIdMap.set(t,r)),r}getRemoteIdToIdMap(t){let r=this.iframeRemoteIdToIdMap.get(t);return r||(r=new Map,this.iframeRemoteIdToIdMap.set(t,r)),r}}class ha{constructor(t){I(this,"iframes",new WeakMap),I(this,"crossOriginIframeMap",new WeakMap),I(this,"crossOriginIframeMirror",new fi(xi)),I(this,"crossOriginIframeStyleMirror"),I(this,"crossOriginIframeRootIdMap",new WeakMap),I(this,"mirror"),I(this,"mutationCb"),I(this,"wrappedEmit"),I(this,"loadListener"),I(this,"stylesheetManager"),I(this,"recordCrossOriginIframes"),this.mutationCb=t.mutationCb,this.wrappedEmit=t.wrappedEmit,this.stylesheetManager=t.stylesheetManager,this.recordCrossOriginIframes=t.recordCrossOriginIframes,this.crossOriginIframeStyleMirror=new fi(this.stylesheetManager.styleMirror.generateId.bind(this.stylesheetManager.styleMirror)),this.mirror=t.mirror,this.recordCrossOriginIframes&&window.addEventListener("message",this.handleMessage.bind(this))}addIframe(t){this.iframes.set(t,!0),t.contentWindow&&this.crossOriginIframeMap.set(t.contentWindow,t)}addLoadListener(t){this.loadListener=t}attachIframe(t,r){var l,s;this.mutationCb({adds:[{parentId:this.mirror.getId(t),nextId:null,node:r}],removes:[],texts:[],attributes:[],isAttachIframe:!0}),this.recordCrossOriginIframes&&((l=t.contentWindow)==null||l.addEventListener("message",this.handleMessage.bind(this))),(s=this.loadListener)==null||s.call(this,t),t.contentDocument&&t.contentDocument.adoptedStyleSheets&&t.contentDocument.adoptedStyleSheets.length>0&&this.stylesheetManager.adoptStyleSheets(t.contentDocument.adoptedStyleSheets,this.mirror.getId(t.contentDocument))}handleMessage(t){const r=t;if(r.data.type!=="rrweb"||r.origin!==r.data.origin||!t.source)return;const s=this.crossOriginIframeMap.get(t.source);if(!s)return;const f=this.transformCrossOriginEvent(s,r.data.event);f&&this.wrappedEmit(f,r.data.isCheckout)}transformCrossOriginEvent(t,r){var l;switch(r.type){case U.FullSnapshot:{this.crossOriginIframeMirror.reset(t),this.crossOriginIframeStyleMirror.reset(t),this.replaceIdOnNode(r.data.node,t);const s=r.data.node.id;return this.crossOriginIframeRootIdMap.set(t,s),this.patchRootIdOnNode(r.data.node,s),{timestamp:r.timestamp,type:U.IncrementalSnapshot,data:{source:D.Mutation,adds:[{parentId:this.mirror.getId(t),nextId:null,node:r.data.node}],removes:[],texts:[],attributes:[],isAttachIframe:!0}}}case U.Meta:case U.Load:case U.DomContentLoaded:return!1;case U.Plugin:return r;case U.Custom:return this.replaceIds(r.data.payload,t,["id","parentId","previousId","nextId"]),r;case U.IncrementalSnapshot:switch(r.data.source){case D.Mutation:return r.data.adds.forEach(s=>{this.replaceIds(s,t,["parentId","nextId","previousId"]),this.replaceIdOnNode(s.node,t);const f=this.crossOriginIframeRootIdMap.get(t);f&&this.patchRootIdOnNode(s.node,f)}),r.data.removes.forEach(s=>{this.replaceIds(s,t,["parentId","id"])}),r.data.attributes.forEach(s=>{this.replaceIds(s,t,["id"])}),r.data.texts.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.Drag:case D.TouchMove:case D.MouseMove:return r.data.positions.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.ViewportResize:return!1;case D.MediaInteraction:case D.MouseInteraction:case D.Scroll:case D.CanvasMutation:case D.Input:return this.replaceIds(r.data,t,["id"]),r;case D.StyleSheetRule:case D.StyleDeclaration:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleId"]),r;case D.Font:return r;case D.Selection:return r.data.ranges.forEach(s=>{this.replaceIds(s,t,["start","end"])}),r;case D.AdoptedStyleSheet:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleIds"]),(l=r.data.styles)==null||l.forEach(s=>{this.replaceStyleIds(s,t,["styleId"])}),r}}return!1}replace(t,r,l,s){for(const f of s)!Array.isArray(r[f])&&typeof r[f]!="number"||(Array.isArray(r[f])?r[f]=t.getIds(l,r[f]):r[f]=t.getId(l,r[f]));return r}replaceIds(t,r,l){return this.replace(this.crossOriginIframeMirror,t,r,l)}replaceStyleIds(t,r,l){return this.replace(this.crossOriginIframeStyleMirror,t,r,l)}replaceIdOnNode(t,r){this.replaceIds(t,r,["id","rootId"]),"childNodes"in t&&t.childNodes.forEach(l=>{this.replaceIdOnNode(l,r)})}patchRootIdOnNode(t,r){t.type!==Qi.Document&&!t.rootId&&(t.rootId=r),"childNodes"in t&&t.childNodes.forEach(l=>{this.patchRootIdOnNode(l,r)})}}class fa{constructor(t){I(this,"shadowDoms",new WeakSet),I(this,"mutationCb"),I(this,"scrollCb"),I(this,"bypassOptions"),I(this,"mirror"),I(this,"restoreHandlers",[]),this.mutationCb=t.mutationCb,this.scrollCb=t.scrollCb,this.bypassOptions=t.bypassOptions,this.mirror=t.mirror,this.init()}init(){this.reset(),this.patchAttachShadow(Element,document)}addShadowRoot(t,r){if(!Fe(t)||this.shadowDoms.has(t))return;this.shadowDoms.add(t);const l=Ki({...this.bypassOptions,doc:r,mutationCb:this.mutationCb,mirror:this.mirror,shadowDomManager:this},t);this.restoreHandlers.push(()=>l.disconnect()),this.restoreHandlers.push(Zi({...this.bypassOptions,scrollCb:this.scrollCb,doc:t,mirror:this.mirror})),setTimeout(()=>{t.adoptedStyleSheets&&t.adoptedStyleSheets.length>0&&this.bypassOptions.stylesheetManager.adoptStyleSheets(t.adoptedStyleSheets,this.mirror.getId(L.host(t))),this.restoreHandlers.push(en({mirror:this.mirror,stylesheetManager:this.bypassOptions.stylesheetManager},t))},0)}observeAttachShadow(t){!t.contentWindow||!t.contentDocument||this.patchAttachShadow(t.contentWindow.Element,t.contentDocument)}patchAttachShadow(t,r){const l=this;this.restoreHandlers.push(Le(t.prototype,"attachShadow",function(s){return function(f){const u=s.call(this,f),m=L.shadowRoot(this);return m&&Yi(this)&&l.addShadowRoot(m,r),u}}))}reset(){this.restoreHandlers.forEach(t=>{try{t()}catch{}}),this.restoreHandlers=[],this.shadowDoms=new WeakSet}}var Pe="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",pa=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var Ze=0;Ze>2],s+=Pe[(t[r]&3)<<4|t[r+1]>>4],s+=Pe[(t[r+1]&15)<<2|t[r+2]>>6],s+=Pe[t[r+2]&63];return l%3===2?s=s.substring(0,s.length-1)+"=":l%3===1&&(s=s.substring(0,s.length-2)+"=="),s};const pi=new Map;function ma(e,t){let r=pi.get(e);return r||(r=new Map,pi.set(e,r)),r.has(t)||r.set(t,[]),r.get(t)}const tn=(e,t,r)=>{if(!e||!(sn(e,t)||typeof e=="object"))return;const l=e.constructor.name,s=ma(r,l);let f=s.indexOf(e);return f===-1&&(f=s.length,s.push(e)),f};function et(e,t,r){if(e instanceof Array)return e.map(l=>et(l,t,r));if(e===null)return e;if(e instanceof Float32Array||e instanceof Float64Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Uint8Array||e instanceof Uint16Array||e instanceof Int16Array||e instanceof Int8Array||e instanceof Uint8ClampedArray)return{rr_type:e.constructor.name,args:[Object.values(e)]};if(e instanceof ArrayBuffer){const l=e.constructor.name,s=da(e);return{rr_type:l,base64:s}}else{if(e instanceof DataView)return{rr_type:e.constructor.name,args:[et(e.buffer,t,r),e.byteOffset,e.byteLength]};if(e instanceof HTMLImageElement){const l=e.constructor.name,{src:s}=e;return{rr_type:l,src:s}}else if(e instanceof HTMLCanvasElement){const l="HTMLImageElement",s=e.toDataURL();return{rr_type:l,src:s}}else{if(e instanceof ImageData)return{rr_type:e.constructor.name,args:[et(e.data,t,r),e.width,e.height]};if(sn(e,t)||typeof e=="object"){const l=e.constructor.name,s=tn(e,t,r);return{rr_type:l,index:s}}}}return e}const rn=(e,t,r)=>e.map(l=>et(l,t,r)),sn=(e,t)=>!!["WebGLActiveInfo","WebGLBuffer","WebGLFramebuffer","WebGLProgram","WebGLRenderbuffer","WebGLShader","WebGLShaderPrecisionFormat","WebGLTexture","WebGLUniformLocation","WebGLVertexArrayObject","WebGLVertexArrayObjectOES"].filter(s=>typeof t[s]=="function").find(s=>e instanceof t[s]);function ga(e,t,r,l){const s=[],f=Object.getOwnPropertyNames(t.CanvasRenderingContext2D.prototype);for(const u of f)try{if(typeof t.CanvasRenderingContext2D.prototype[u]!="function")continue;const m=Le(t.CanvasRenderingContext2D.prototype,u,function(a){return function(...p){return re(this.canvas,r,l,!0)||setTimeout(()=>{const i=rn(p,t,this);e(this.canvas,{type:_e["2D"],property:u,args:i})},0),a.apply(this,p)}});s.push(m)}catch{const m=St(t.CanvasRenderingContext2D.prototype,u,{set(a){e(this.canvas,{type:_e["2D"],property:u,args:[a],setter:!0})}});s.push(m)}return()=>{s.forEach(u=>u())}}function ya(e){return e==="experimental-webgl"?"webgl":e}function di(e,t,r,l){const s=[];try{const f=Le(e.HTMLCanvasElement.prototype,"getContext",function(u){return function(m,...a){if(!re(this,t,r,!0)){const p=ya(m);if("__context"in this||(this.__context=p),l&&["webgl","webgl2"].includes(p))if(a[0]&&typeof a[0]=="object"){const i=a[0];i.preserveDrawingBuffer||(i.preserveDrawingBuffer=!0)}else a.splice(0,1,{preserveDrawingBuffer:!0})}return u.apply(this,[m,...a])}});s.push(f)}catch{console.error("failed to patch HTMLCanvasElement.prototype.getContext")}return()=>{s.forEach(f=>f())}}function mi(e,t,r,l,s,f){const u=[],m=Object.getOwnPropertyNames(e);for(const a of m)if(!["isContextLost","canvas","drawingBufferWidth","drawingBufferHeight"].includes(a))try{if(typeof e[a]!="function")continue;const p=Le(e,a,function(i){return function(...c){const o=i.apply(this,c);if(tn(o,f,this),"tagName"in this.canvas&&!re(this.canvas,l,s,!0)){const n=rn(c,f,this),d={type:t,property:a,args:n};r(this.canvas,d)}return o}});u.push(p)}catch{const p=St(e,a,{set(i){r(this.canvas,{type:t,property:a,args:[i],setter:!0})}});u.push(p)}return u}function wa(e,t,r,l){const s=[];return s.push(...mi(t.WebGLRenderingContext.prototype,_e.WebGL,e,r,l,t)),typeof t.WebGL2RenderingContext<"u"&&s.push(...mi(t.WebGL2RenderingContext.prototype,_e.WebGL2,e,r,l,t)),()=>{s.forEach(f=>f())}}const nn=`(function() { - "use strict"; - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256); - for (var i = 0; i < chars.length; i++) { - lookup[chars.charCodeAt(i)] = i; - } - var encode = function(arraybuffer) { - var bytes = new Uint8Array(arraybuffer), i2, len = bytes.length, base64 = ""; - for (i2 = 0; i2 < len; i2 += 3) { - base64 += chars[bytes[i2] >> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`,gi=typeof self<"u"&&self.Blob&&new Blob([nn],{type:"text/javascript;charset=utf-8"});function ba(e){let t;try{if(t=gi&&(self.URL||self.webkitURL).createObjectURL(gi),!t)throw"";const r=new Worker(t,{name:e?.name});return r.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(t)}),r}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(nn),{name:e?.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}class Sa{constructor(t){I(this,"pendingCanvasMutations",new Map),I(this,"rafStamps",{latestId:0,invokeId:null}),I(this,"mirror"),I(this,"mutationCb"),I(this,"resetObservers"),I(this,"frozen",!1),I(this,"locked",!1),I(this,"processMutation",(a,p)=>{(this.rafStamps.invokeId&&this.rafStamps.latestId!==this.rafStamps.invokeId||!this.rafStamps.invokeId)&&(this.rafStamps.invokeId=this.rafStamps.latestId),this.pendingCanvasMutations.has(a)||this.pendingCanvasMutations.set(a,[]),this.pendingCanvasMutations.get(a).push(p)});const{sampling:r="all",win:l,blockClass:s,blockSelector:f,recordCanvas:u,dataURLOptions:m}=t;this.mutationCb=t.mutationCb,this.mirror=t.mirror,u&&r==="all"&&this.initCanvasMutationObserver(l,s,f),u&&typeof r=="number"&&this.initCanvasFPSObserver(r,l,s,f,{dataURLOptions:m})}reset(){this.pendingCanvasMutations.clear(),this.resetObservers&&this.resetObservers()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1}lock(){this.locked=!0}unlock(){this.locked=!1}initCanvasFPSObserver(t,r,l,s,f){const u=di(r,l,s,!0),m=new Map,a=new ba;a.onmessage=d=>{const{id:h}=d.data;if(m.set(h,!1),!("base64"in d.data))return;const{base64:y,type:C,width:w,height:S}=d.data;this.mutationCb({id:h,type:_e["2D"],commands:[{property:"clearRect",args:[0,0,w,S]},{property:"drawImage",args:[{rr_type:"ImageBitmap",args:[{rr_type:"Blob",data:[{rr_type:"ArrayBuffer",base64:y}],type:C}]},0,0]}]})};const p=1e3/t;let i=0,c;const o=()=>{const d=[];return r.document.querySelectorAll("canvas").forEach(h=>{re(h,l,s,!0)||d.push(h)}),d},n=d=>{if(i&&d-i{var y;const C=this.mirror.getId(h);if(m.get(C)||h.width===0||h.height===0)return;if(m.set(C,!0),["webgl","webgl2"].includes(h.__context)){const S=h.getContext(h.__context);((y=S?.getContextAttributes())==null?void 0:y.preserveDrawingBuffer)===!1&&S.clear(S.COLOR_BUFFER_BIT)}const w=await createImageBitmap(h);a.postMessage({id:C,bitmap:w,width:h.width,height:h.height,dataURLOptions:f.dataURLOptions},[w])}),c=requestAnimationFrame(n)};c=requestAnimationFrame(n),this.resetObservers=()=>{u(),cancelAnimationFrame(c)}}initCanvasMutationObserver(t,r,l){this.startRAFTimestamping(),this.startPendingCanvasMutationFlusher();const s=di(t,r,l,!1),f=ga(this.processMutation.bind(this),t,r,l),u=wa(this.processMutation.bind(this),t,r,l);this.resetObservers=()=>{s(),f(),u()}}startPendingCanvasMutationFlusher(){requestAnimationFrame(()=>this.flushPendingCanvasMutations())}startRAFTimestamping(){const t=r=>{this.rafStamps.latestId=r,requestAnimationFrame(t)};requestAnimationFrame(t)}flushPendingCanvasMutations(){this.pendingCanvasMutations.forEach((t,r)=>{const l=this.mirror.getId(r);this.flushPendingCanvasMutationFor(r,l)}),requestAnimationFrame(()=>this.flushPendingCanvasMutations())}flushPendingCanvasMutationFor(t,r){if(this.frozen||this.locked)return;const l=this.pendingCanvasMutations.get(t);if(!l||r===-1)return;const s=l.map(u=>{const{type:m,...a}=u;return a}),{type:f}=l[0];this.mutationCb({id:r,type:f,commands:s}),this.pendingCanvasMutations.delete(t)}}class va{constructor(t){I(this,"trackedLinkElements",new WeakSet),I(this,"mutationCb"),I(this,"adoptedStyleSheetCb"),I(this,"styleMirror",new qo),this.mutationCb=t.mutationCb,this.adoptedStyleSheetCb=t.adoptedStyleSheetCb}attachLinkElement(t,r){"_cssText"in r.attributes&&this.mutationCb({adds:[],removes:[],texts:[],attributes:[{id:r.id,attributes:r.attributes}]}),this.trackLinkElement(t)}trackLinkElement(t){this.trackedLinkElements.has(t)||(this.trackedLinkElements.add(t),this.trackStylesheetInLinkElement(t))}adoptStyleSheets(t,r){if(t.length===0)return;const l={id:r,styleIds:[]},s=[];for(const f of t){let u;this.styleMirror.has(f)?u=this.styleMirror.getId(f):(u=this.styleMirror.add(f),s.push({styleId:u,rules:Array.from(f.rules||CSSRule,(m,a)=>({rule:Si(m,f.href),index:a}))})),l.styleIds.push(u)}s.length>0&&(l.styles=s),this.adoptedStyleSheetCb(l)}reset(){this.styleMirror.reset(),this.trackedLinkElements=new WeakSet}trackStylesheetInLinkElement(t){}}class Ca{constructor(){I(this,"nodeMap",new WeakMap),I(this,"active",!1)}inOtherBuffer(t,r){const l=this.nodeMap.get(t);return l&&Array.from(l).some(s=>s!==r)}add(t,r){this.active||(this.active=!0,requestAnimationFrame(()=>{this.nodeMap=new WeakMap,this.active=!1})),this.nodeMap.set(t,(this.nodeMap.get(t)||new Set).add(r))}destroy(){}}let J,tt,Or,ut=!1;try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((Jr=e.contentWindow)==null?void 0:Jr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}const ce=In();function Oe(e={}){const{emit:t,checkoutEveryNms:r,checkoutEveryNth:l,blockClass:s="rr-block",blockSelector:f=null,ignoreClass:u="rr-ignore",ignoreSelector:m=null,maskTextClass:a="rr-mask",maskTextSelector:p=null,inlineStylesheet:i=!0,maskAllInputs:c,maskInputOptions:o,slimDOMOptions:n,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,hooks:C,packFn:w,sampling:S={},dataURLOptions:b={},mousemoveWait:g,recordDOM:v=!0,recordCanvas:x=!1,recordCrossOriginIframes:O=!1,recordAfter:A=e.recordAfter==="DOMContentLoaded"?e.recordAfter:"load",userTriggeredOnInput:M=!1,collectFonts:$=!1,inlineImages:k=!1,plugins:R,keepIframeSrcFn:Z=()=>!1,ignoreCSSAttributes:N=new Set([]),errorHandler:ee,applyBackgroundColorToBlockedElements:H=!1}=e;Qo(ee);const F=O?window.parent===window:!0;let Q=!1;if(!F)try{window.parent.document&&(Q=!1)}catch{Q=!0}if(F&&!t)throw new Error("emit function is required");if(!F&&!Q)return()=>{};g!==void 0&&S.mousemove===void 0&&(S.mousemove=g),ce.reset();const W=c===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:o!==void 0?o:{password:!0},P=n===!0||n==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaVerification:!0,headMetaAuthorship:n==="all",headMetaDescKeywords:n==="all",headTitleMutations:n==="all"}:n||{};Wo();let ye,E=0;const pe=_=>{for(const ue of R||[])ue.eventProcessor&&(_=ue.eventProcessor(_));return w&&!Q&&(_=w(_)),_};J=(_,ue)=>{var G;const V=_;if(V.timestamp=at(),(G=ve[0])!=null&&G.isFrozen()&&V.type!==U.FullSnapshot&&!(V.type===U.IncrementalSnapshot&&V.data.source===D.Mutation)&&ve.forEach(me=>me.unfreeze()),F)t?.(pe(V),ue);else if(Q){const me={type:"rrweb",event:pe(V),origin:window.location.origin,isCheckout:ue};window.parent.postMessage(me,"*")}if(V.type===U.FullSnapshot)ye=V,E=0;else if(V.type===U.IncrementalSnapshot){if(V.data.source===D.Mutation&&V.data.isAttachIframe)return;E++;const me=l&&E>=l,B=r&&V.timestamp-ye.timestamp>r;(me||B)&&tt(!0)}};const se=_=>{J({type:U.IncrementalSnapshot,data:{source:D.Mutation,..._}})},Me=_=>J({type:U.IncrementalSnapshot,data:{source:D.Scroll,..._}}),de=_=>J({type:U.IncrementalSnapshot,data:{source:D.CanvasMutation,..._}}),De=_=>J({type:U.IncrementalSnapshot,data:{source:D.AdoptedStyleSheet,..._}}),ae=new va({mutationCb:se,adoptedStyleSheetCb:De}),le=new ha({mirror:ce,mutationCb:se,stylesheetManager:ae,recordCrossOriginIframes:O,wrappedEmit:J});for(const _ of R||[])_.getMirror&&_.getMirror({nodeMirror:ce,crossOriginIframeMirror:le.crossOriginIframeMirror,crossOriginIframeStyleMirror:le.crossOriginIframeStyleMirror});const X=new Ca;Or=new Sa({recordCanvas:x,mutationCb:de,win:window,blockClass:s,blockSelector:f,mirror:ce,sampling:S.canvas,dataURLOptions:b});const ne=new fa({mutationCb:se,scrollCb:Me,bypassOptions:{blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskInputOptions:W,dataURLOptions:b,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,recordCanvas:x,inlineImages:k,sampling:S,slimDOMOptions:P,iframeManager:le,stylesheetManager:ae,canvasManager:Or,keepIframeSrcFn:Z,processedNodeManager:X},mirror:ce});tt=(_=!1)=>{if(!v)return;J({type:U.Meta,data:{href:window.location.href,width:qi(),height:Wi()}},_),ae.reset(),ne.init(),ve.forEach(G=>G.lock());const ue=Zn(document,{mirror:ce,blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskAllInputs:W,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,slimDOM:P,dataURLOptions:b,recordCanvas:x,inlineImages:k,applyBackgroundColorToBlockedElements:H,onSerialize:G=>{Gi(G,ce)&&le.addIframe(G),Vi(G,ce)&&ae.trackLinkElement(G),Ir(G)&&ne.addShadowRoot(L.shadowRoot(G),document)},onIframeLoad:(G,V)=>{le.attachIframe(G,V),ne.observeAttachShadow(G)},onStylesheetLoad:(G,V)=>{ae.attachLinkElement(G,V)},keepIframeSrcFn:Z});if(!ue)return console.warn("Failed to snapshot the document");J({type:U.FullSnapshot,data:{node:ue,initialOffset:zi(window)}},_),ve.forEach(G=>G.unlock()),document.adoptedStyleSheets&&document.adoptedStyleSheets.length>0&&ae.adoptStyleSheets(document.adoptedStyleSheets,ce.getId(document))};try{const _=[],ue=V=>{var me;return T(ca)({mutationCb:se,mousemoveCb:(B,vt)=>J({type:U.IncrementalSnapshot,data:{source:vt,positions:B}}),mouseInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MouseInteraction,...B}}),scrollCb:Me,viewportResizeCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.ViewportResize,...B}}),inputCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Input,...B}}),mediaInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MediaInteraction,...B}}),styleSheetRuleCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleSheetRule,...B}}),styleDeclarationCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleDeclaration,...B}}),canvasMutationCb:de,fontCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Font,...B}}),selectionCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.Selection,...B}})},customElementCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.CustomElement,...B}})},blockClass:s,ignoreClass:u,ignoreSelector:m,maskTextClass:a,maskTextSelector:p,maskInputOptions:W,inlineStylesheet:i,sampling:S,recordDOM:v,recordCanvas:x,inlineImages:k,userTriggeredOnInput:M,collectFonts:$,doc:V,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,keepIframeSrcFn:Z,blockSelector:f,slimDOMOptions:P,dataURLOptions:b,mirror:ce,iframeManager:le,stylesheetManager:ae,shadowDomManager:ne,processedNodeManager:X,canvasManager:Or,ignoreCSSAttributes:N,plugins:((me=R?.filter(B=>B.observer))==null?void 0:me.map(B=>({observer:B.observer,options:B.options,callback:vt=>J({type:U.Plugin,data:{plugin:B.name,payload:vt}})})))||[]},C)};le.addLoadListener(V=>{try{_.push(ue(V.contentDocument))}catch(me){console.warn(me)}});const G=()=>{tt(),_.push(ue(document)),ut=!0};return document.readyState==="interactive"||document.readyState==="complete"?G():(_.push(te("DOMContentLoaded",()=>{J({type:U.DomContentLoaded,data:{}}),A==="DOMContentLoaded"&&G()})),_.push(te("load",()=>{J({type:U.Load,data:{}}),A==="load"&&G()},window))),()=>{_.forEach(V=>V()),X.destroy(),ut=!1,Xo()}}catch(_){console.warn(_)}}Oe.addCustomEvent=(e,t)=>{if(!ut)throw new Error("please add custom event after start recording");J({type:U.Custom,data:{tag:e,payload:t}})};Oe.freezePage=()=>{ve.forEach(e=>e.freeze())};Oe.takeFullSnapshot=e=>{if(!ut)throw new Error("please take full snapshot after start recording");tt(e)};Oe.mirror=ce;var yi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(yi||(yi={}));const{addCustomEvent:xa}=Oe,{freezePage:Ra}=Oe,{takeFullSnapshot:Oa}=Oe;export{Oe as record}; - -``` - ---- - -## .output/public/_nuxt/DOp-_5WN.js - -```js -import{j as m,k as p,c as i,a as s,l as n,t as u,p as b,r as f,x as h,z as _,o as l}from"./BpsV6fwO.js";const C={class:"container"},v={style:{"text-align":"center"}},y={key:0,class:"success"},k={class:"stats"},B=m({__name:"burrito",setup(T){const a=p(),o=f(()=>a.user.value),c=_(),r=h(!1),d=async()=>{if(o.value)try{const e=await $fetch("/api/burrito/consider",{method:"POST",body:{username:o.value.username}});e.success&&e.user&&(a.setUser(e.user),r.value=!0,c?.track("burrito_considered",{total_considerations:e.user.burritoConsiderations,username:e.user.username}),setTimeout(()=>{r.value=!1},2e3))}catch(e){console.error("Error considering burrito:",e)}};return(e,t)=>(l(),i("div",C,[t[1]||(t[1]=s("h1",null,"Burrito consideration zone",-1)),t[2]||(t[2]=s("p",null,"Take a moment to truly consider the potential of burritos.",-1)),s("div",v,[s("button",{onClick:d,class:"btn-burrito"}," I have considered the burrito potential "),n(r)?(l(),i("p",y," Thank you for your consideration! Count: "+u(n(o)?.burritoConsiderations),1)):b("",!0)]),s("div",k,[t[0]||(t[0]=s("h3",null,"Consideration stats",-1)),s("p",null,"Total considerations: "+u(n(o)?.burritoConsiderations),1)])]))}});export{B as default}; - -``` - ---- - -## .output/public/_nuxt/iMBneS3o.js - -```js -import{A as t,k as e,y as u}from"./BpsV6fwO.js";const f=t((a,s)=>{if(!e().user.value)return u("/")});export{f as default}; - -``` - ---- - -## .output/public/_nuxt/PJVKHxNq.js - -```js -import{B as N,C as A,E as U,F as O}from"./BpsV6fwO.js";var L=function(a,e){return L=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,t){r.__proto__=t}||function(r,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(r[n]=t[n])},L(a,e)};function k(a,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");L(a,e);function r(){this.constructor=a}a.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var d=function(){return d=Object.assign||function(e){for(var r,t=1,n=arguments.length;t=a.length&&(a=void 0),{value:a&&a[t++],done:!a}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function D(a,e){var r=typeof Symbol=="function"&&a[Symbol.iterator];if(!r)return a;var t=r.call(a),n,o=[],i;try{for(;(e===void 0||e-- >0)&&!(n=t.next()).done;)o.push(n.value)}catch(c){i={error:c}}finally{try{n&&!n.done&&(r=t.return)&&r.call(t)}finally{if(i)throw i.error}}return o}function P(a,e,r){if(arguments.length===2)for(var t=0,n=e.length,o;t>6|192,e[r++]=n&63|128):(n&64512)==55296&&t+1>18|240,e[r++]=n>>12&63|128,e[r++]=n>>6&63|128,e[r++]=n&63|128):(e[r++]=n>>12|224,e[r++]=n>>6&63|128,e[r++]=n&63|128)}return Uint8Array.from(e)},R=-862048943,I=461845907,C=15,M=13,Q=5,B=-430675100,F=function(a,e){e===void 0&&(e=0);for(var r=j(a),t=r.length,n=t>>2,o=e,i=0;i>>0},X=function(a,e){var r=a,t=e;return r=Math.imul(r,R),r=m(r,C),r=Math.imul(r,I),t^=r,t=m(t,M),t=Math.imul(t,Q),t+B|0},Y=function(a){var e=a;return e^=e>>>16,e=Math.imul(e,-2048144789),e^=e>>>13,e=Math.imul(e,-1028477387),e^=e>>>16,e},m=function(a,e,r){r===void 0&&(r=32),e>r&&(e=e%r);var t=4294967295<>>0,n=(a&t)>>>0>>>r-e>>>0;return(a<>>0},q=function(a,e){e===void 0&&(e=0);var r=a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3];return J(r)},J=function(a){return(a&-16777216)>>>24|(a&16711680)>>>8|(a&65280)<<8|(a&255)<<24},H=function(a,e){var r,t;if(!(!e||e.length===0)){try{for(var n=y(e),o=n.next();!o.done;o=n.next()){var i=o.value;if(!i||!a||typeof a!="object")return;a=a[i]}}catch(c){r={error:c}}finally{try{o&&!o.done&&(t=n.return)&&t.call(n)}finally{if(r)throw r.error}}if(a)return a}},z="(\\d+)\\.(\\d+)",K="(\\d+)",$="(-(([-\\w]+\\.?)*))?",W="^".concat(z,"(\\.").concat(K).concat($,")?$"),Z=(function(){function a(e,r,t,n){n===void 0&&(n=void 0),this.major=e,this.minor=r,this.patch=t,this.preRelease=n}return a.parse=function(e){if(e){var r=new RegExp(W).exec(e);if(r){var t=Number(r[1]),n=Number(r[2]);if(!(isNaN(t)||isNaN(n))){var o=Number(r[4])||0,i=r[5]||void 0;return new a(t,n,o,i)}}}},a.prototype.compareTo=function(e){return this.major>e.major?1:this.majore.minor?1:this.minore.patch?1:this.patche.preRelease?1:this.preRelease=l&&u=b&&St;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return e>=t;default:return!1}},a.prototype.versionComparator=function(e,r,t){var n=e.compareTo(t);switch(r){case s.LESS_THAN:case s.VERSION_LESS_THAN:return n<0;case s.LESS_THAN_EQUALS:case s.VERSION_LESS_THAN_EQUALS:return n<=0;case s.GREATER_THAN:case s.VERSION_GREATER_THAN:return n>0;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return n>=0;default:return!1}},a.prototype.matchesRegex=function(e,r){return r.some(function(t){return!!new RegExp(t).exec(e)})},a.prototype.containsNone=function(e){return e.some(function(r){return r==="(none)"})},a.prototype.containsBooleans=function(e){return e.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},a.prototype.parseNumber=function(e){var r;return(r=Number(e))!==null&&r!==void 0?r:void 0},a.prototype.coerceString=function(e){if(e)return typeof e=="object"?JSON.stringify(e):String(e)},a.prototype.coerceStringArray=function(e){var r=this;if(Array.isArray(e)){var t=e;return t.map(function(i){return r.coerceString(i)}).filter(Boolean)}var n=String(e);try{var o=JSON.parse(n);if(Array.isArray(o)){var t=e;return t.map(function(c){return r.coerceString(c)}).filter(Boolean)}else return}catch{return}},a.prototype.isSetOperator=function(e){switch(e){case s.SET_IS:case s.SET_IS_NOT:case s.SET_CONTAINS:case s.SET_DOES_NOT_CONTAIN:case s.SET_CONTAINS_ANY:case s.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},a.prototype.setEquals=function(e,r){var t=new Set(e),n=new Set(r);return t.size===n.size&&P([],D(n),!1).every(function(o){return t.has(o)})},a.prototype.matchesSetContainsAll=function(e,r){var t,n;if(e.length{let e={};return a.forEach((r,t)=>e[r]=t),e})(re);String.fromCharCode.bind(String);typeof Uint8Array.from=="function"&&Uint8Array.from.bind(Uint8Array);(function(a){k(e,a);function e(r,t){var n=a.call(this,t)||this;return n.statusCode=r,Object.setPrototypeOf(n,e.prototype),n}return e})(Error);var te=1e3*60*60*24*2,ne=(function(){function a(){var e=this;this.dbs={},this.createStore=function(r){return N(e,void 0,void 0,function(){return A(this,function(t){switch(t.label){case 0:return[4,U(r,1,{upgrade:function(n){n.objectStoreNames.contains("eventTypesForSession")||n.createObjectStore("eventTypesForSession",{keyPath:"sessionId"})}})];case 1:return[2,t.sent()]}})})},this.openOrCreateDB=function(r){return N(e,void 0,void 0,function(){var t,n;return A(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(t="".concat(r.substring(0,10),"_amp_targeting"),[4,this.createStore(t)]);case 1:return n=o.sent(),this.dbs[r]=n,[2,n]}})})},this.updateEventListForSession=function(r){var t=r.sessionId,n=r.eventType,o=r.eventTime,i=r.loggerProvider,c=r.tx;return N(e,void 0,void 0,function(){var v,f,u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,c.store.get(t)];case 1:return v=l.sent(),f=v?v.eventTypes:{},u=f[n]||{},S=O(O({},f),(E={},E[n]=O(O({},u),(T={},T[o]={event_type:n},T)),E)),[4,c.store.put({sessionId:t,eventTypes:S})];case 2:return l.sent(),[2,S];case 3:return h=l.sent(),i.warn("Failed to store events for targeting ".concat(t,": ").concat(h)),[3,4];case 4:return[2,void 0]}})})},this.deleteOldSessionEventTypes=function(r){var t=r.currentSessionId,n=r.loggerProvider,o=r.tx;return N(e,void 0,void 0,function(){var i,c,v,f,u;return A(this,function(S){switch(S.label){case 0:return S.trys.push([0,6,,7]),[4,o.store.getAll()];case 1:i=S.sent(),c=0,S.label=2;case 2:return cte?[4,o.store.delete(v.sessionId)]:[3,4]):[3,5];case 3:S.sent(),S.label=4;case 4:return c++,[3,2];case 5:return[3,7];case 6:return u=S.sent(),n.warn("Failed to clear old session events for targeting: ".concat(u)),[3,7];case 7:return[2]}})})},this.storeEventTypeForSession=function(r){var t=r.loggerProvider,n=r.sessionId,o=r.eventType,i=r.eventTime,c=r.apiKey;return N(e,void 0,void 0,function(){var v,f,u,S;return A(this,function(h){switch(h.label){case 0:return h.trys.push([0,5,,6]),[4,this.openOrCreateDB(c)];case 1:return v=h.sent(),f=v.transaction("eventTypesForSession","readwrite"),f?[4,this.updateEventListForSession({sessionId:n,tx:f,loggerProvider:t,eventType:o,eventTime:i})]:[2];case 2:return u=h.sent(),[4,this.deleteOldSessionEventTypes({currentSessionId:n,tx:f,loggerProvider:t})];case 3:return h.sent(),[4,f.done];case 4:return h.sent(),[2,u];case 5:return S=h.sent(),t.warn("Failed to store events for targeting ".concat(n,": ").concat(S)),[3,6];case 6:return[2,void 0]}})})}}return a})(),ae=new ne,oe=(function(){function a(){var e=this;this.evaluateTargeting=function(r){var t=r.apiKey,n=r.loggerProvider,o=r.event,i=r.sessionId,c=r.userProperties,v=r.deviceId,f=r.flag;return N(e,void 0,void 0,function(){var u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return o&&o.time?[4,ae.storeEventTypeForSession({loggerProvider:n,apiKey:t,sessionId:i,eventType:o.event_type,eventTime:o.time})]:[3,2];case 1:return S=l.sent(),[3,3];case 2:S=void 0,l.label=3;case 3:return u=S,h=u&&new Set(Object.keys(u)),E={session_id:i,event:o,event_types:h&&Array.from(h),user:{device_id:v,user_properties:c}},T=this.evaluationEngine.evaluate(E,[f]),[2,T]}})})},this.evaluationEngine=new V}return a})(),ie=function(){var a=new oe;return{evaluateTargeting:a.evaluateTargeting.bind(a)}};const se=ie();var ce=se.evaluateTargeting;export{ce as evaluateTargeting}; - -``` - ---- - -## .output/public/_nuxt/sPdhR0i8.js - -```js -var me={},ki=Object.defineProperty,qi=(e,o,p)=>o in e?ki(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,le=(e,o,p)=>qi(e,typeof o!="symbol"?o+"":o,p),Fi=Object.defineProperty,Bi=(e,o,p)=>o in e?Fi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,_e=(e,o,p)=>Bi(e,typeof o!="symbol"?o+"":o,p),tr,zi=Object.defineProperty,ji=(e,o,p)=>o in e?zi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,rr=(e,o,p)=>ji(e,typeof o!="symbol"?o+"":o,p);const ir={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},sr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},ae={};function Wi(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function kt(e){if(ae[e])return ae[e];const o=Wi(e)||globalThis[e],p=o.prototype,d=e in ir?ir[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in sr?sr[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return ae[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?ae[e]=h:p}catch{return p}}const Ie={};function W(e,o,p){var d;const s=`${e}.${String(p)}`;if(Ie[s])return Ie[s].call(o);const f=kt(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(Ie[s]=a,a.call(o)):o[p]}const Le={};function pi(e,o,p){const d=`${e}.${String(p)}`;if(Le[d])return Le[d].bind(o);const f=kt(e)[p];return typeof f!="function"?o[p]:(Le[d]=f,f.bind(o))}function Hi(e){return W("Node",e,"childNodes")}function Gi(e){return W("Node",e,"parentNode")}function Ji(e){return W("Node",e,"parentElement")}function Vi(e){return W("Node",e,"textContent")}function Ki(e,o){return pi("Node",e,"contains")(o)}function Qi(e){return pi("Node",e,"getRootNode")()}function Yi(e){return!e||!("host"in e)?null:W("ShadowRoot",e,"host")}function Xi(e){return e.styleSheets}function Zi(e){return!e||!("shadowRoot"in e)?null:W("Element",e,"shadowRoot")}function es(e,o){return W("Element",e,"querySelector")(o)}function ts(e,o){return W("Element",e,"querySelectorAll")(o)}function rs(){return kt("MutationObserver").constructor}const ge={childNodes:Hi,parentNode:Gi,parentElement:Ji,textContent:Vi,contains:Ki,getRootNode:Qi,host:Yi,styleSheets:Xi,shadowRoot:Zi,querySelector:es,querySelectorAll:ts,mutationObserver:rs};function is(e){const o=e&&"host"in e&&"mode"in e&&ge.host(e)||null;return!!(o&&"shadowRoot"in o&&ge.shadowRoot(o)===e)}class ss{constructor(){rr(this,"idNodeMap",new Map),rr(this,"nodeMetaMap",new WeakMap)}getId(o){var p;return o?((p=this.getMeta(o))==null?void 0:p.id)??-1:-1}getNode(o){return this.idNodeMap.get(o)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(o){return this.nodeMetaMap.get(o)||null}removeNodeFromMap(o){const p=this.getId(o);this.idNodeMap.delete(p),o.childNodes&&o.childNodes.forEach(d=>this.removeNodeFromMap(d))}has(o){return this.idNodeMap.has(o)}hasNode(o){return this.nodeMetaMap.has(o)}add(o,p){const d=p.id;this.idNodeMap.set(d,o),this.nodeMetaMap.set(o,p)}replace(o,p){const d=this.getNode(o);if(d){const s=this.nodeMetaMap.get(d);s&&this.nodeMetaMap.set(p,s)}this.idNodeMap.set(o,p)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function ns(){return new ss}const os=-2;function Tt(e,o,p){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return p?Tt(ge.parentNode(e),o,p):!1;for(let d=e.classList.length;d--;){const s=e.classList[d];if(o.test(s))return!0}return p?Tt(ge.parentNode(e),o,p):!1}function ls(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function as(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var ue={exports:{}},nr;function us(){if(nr)return ue.exports;nr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return ue.exports=o(),ue.exports.createColors=o,ue.exports}const fs={},hs=Object.freeze(Object.defineProperty({__proto__:null,default:fs},Symbol.toStringTag,{value:"Module"})),F=as(hs);var $e,or;function qt(){if(or)return $e;or=1;let e=us(),o=F;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return $e=p,p.default=p,$e}var fe={},lr;function Ft(){return lr||(lr=1,fe.isClean=Symbol("isClean"),fe.my=Symbol("my")),fe}var Te,ar;function di(){if(ar)return Te;ar=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Te=p,p.default=p,Te}var Ue,ur;function ye(){if(ur)return Ue;ur=1;let e=di();function o(p,d){new e(d).stringify(p)}return Ue=o,o.default=o,Ue}var De,fr;function we(){if(fr)return De;fr=1;let{isClean:e,my:o}=Ft(),p=qt(),d=di(),s=ye();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return De=a,a.default=a,De}var ke,hr;function be(){if(hr)return ke;hr=1;let e=we();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ke=o,o.default=o,ke}var qe,cr;function cs(){if(cr)return qe;cr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return qe={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},qe}var Fe,pr;function mi(){if(pr)return Fe;pr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{existsSync:p,readFileSync:d}=F,{dirname:s,join:f}=F;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return Fe=m,m.default=m,Fe}var Be,dr;function ve(){if(dr)return Be;dr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{fileURLToPath:p,pathToFileURL:d}=F,{isAbsolute:s,resolve:f}=F,{nanoid:a}=cs(),m=F,n=qt(),h=mi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return Be=r,r.default=r,m&&m.registerInput&&m.registerInput(r),Be}var ze,mr;function gi(){if(mr)return ze;mr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{dirname:p,relative:d,resolve:s,sep:f}=F,{pathToFileURL:a}=F,m=ve(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return ze=t,ze}var je,gr;function xe(){if(gr)return je;gr=1;let e=we();class o extends e{constructor(d){super(d),this.type="comment"}}return je=o,o.default=o,je}var We,yr;function Q(){if(yr)return We;yr=1;let{isClean:e,my:o}=Ft(),p=be(),d=xe(),s=we(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},We=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},We}var He,wr;function Bt(){if(wr)return He;wr=1;let e=Q(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},He=d,d.default=d,He}var Ge,br;function yi(){if(br)return Ge;br=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Ge=e,e.default=e,Ge}var Je,vr;function zt(){if(vr)return Je;vr=1;let e=yi();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return Je=o,o.default=o,Je}var Ve,xr;function ps(){if(xr)return Ve;xr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return Ve=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},Ve}var Ke,Sr;function jt(){if(Sr)return Ke;Sr=1;let e=Q();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return Ke=o,o.default=o,e.registerAtRule(o),Ke}var Qe,Rr;function ne(){if(Rr)return Qe;Rr=1;let e=Q(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},Qe=d,d.default=d,e.registerRoot(d),Qe}var Ye,Or;function wi(){if(Or)return Ye;Or=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Ye=e,e.default=e,Ye}var Xe,Cr;function Wt(){if(Cr)return Xe;Cr=1;let e=Q(),o=wi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Xe=p,p.default=p,e.registerRule(p),Xe}var Ze,Er;function ds(){if(Er)return Ze;Er=1;let e=be(),o=ps(),p=xe(),d=jt(),s=ne(),f=Wt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},tt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),tt}var rt,Nr;function ms(){if(Nr)return rt;Nr=1;let e=gi(),o=ye(),p=Ht();const d=zt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return rt=s,s.default=s,rt}var it,Mr;function gs(){if(Mr)return it;Mr=1;let e=ms(),o=bi(),p=Bt(),d=ne();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return it=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),it}var st,_r;function ys(){if(_r)return st;_r=1;let e=be(),o=mi(),p=xe(),d=jt(),s=ve(),f=ne(),a=Wt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return st=m,m.default=m,st}var nt,Ir;function ws(){if(Ir)return nt;Ir=1;let e=qt(),o=be(),p=bi(),d=Q(),s=gs(),f=ye(),a=ys(),m=Bt(),n=yi(),h=xe(),t=jt(),l=zt(),i=ve(),r=Ht(),c=wi(),u=Wt(),g=ne(),S=we();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),nt=b,b.default=b,nt}var bs=ws();const _=ls(bs);_.stringify;_.fromJSON;_.plugin;_.parse;_.list;_.document;_.comment;_.atRule;_.rule;_.decl;_.root;_.CssSyntaxError;_.Declaration;_.Container;_.Processor;_.Document;_.Comment;_.Warning;_.AtRule;_.Result;_.Input;_.Rule;_.Root;_.Node;var vs=Object.defineProperty,xs=(e,o,p)=>o in e?vs(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,k=(e,o,p)=>xs(e,typeof o!="symbol"?o+"":o,p);function Ss(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Rs(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var he={exports:{}},Lr;function Os(){if(Lr)return he.exports;Lr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return he.exports=o(),he.exports.createColors=o,he.exports}const Cs={},Es=Object.freeze(Object.defineProperty({__proto__:null,default:Cs},Symbol.toStringTag,{value:"Module"})),B=Rs(Es);var ot,$r;function Gt(){if($r)return ot;$r=1;let e=Os(),o=B;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return ot=p,p.default=p,ot}var ce={},Tr;function Jt(){return Tr||(Tr=1,ce.isClean=Symbol("isClean"),ce.my=Symbol("my")),ce}var lt,Ur;function vi(){if(Ur)return lt;Ur=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return lt=p,p.default=p,lt}var at,Dr;function Se(){if(Dr)return at;Dr=1;let e=vi();function o(p,d){new e(d).stringify(p)}return at=o,o.default=o,at}var ut,kr;function Re(){if(kr)return ut;kr=1;let{isClean:e,my:o}=Jt(),p=Gt(),d=vi(),s=Se();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return ut=a,a.default=a,ut}var ft,qr;function Oe(){if(qr)return ft;qr=1;let e=Re();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ft=o,o.default=o,ft}var ht,Fr;function Ps(){if(Fr)return ht;Fr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return ht={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},ht}var ct,Br;function xi(){if(Br)return ct;Br=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{existsSync:p,readFileSync:d}=B,{dirname:s,join:f}=B;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return ct=m,m.default=m,ct}var pt,zr;function Ce(){if(zr)return pt;zr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{fileURLToPath:p,pathToFileURL:d}=B,{isAbsolute:s,resolve:f}=B,{nanoid:a}=Ps(),m=B,n=Gt(),h=xi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return pt=r,r.default=r,m&&m.registerInput&&m.registerInput(r),pt}var dt,jr;function Si(){if(jr)return dt;jr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{dirname:p,relative:d,resolve:s,sep:f}=B,{pathToFileURL:a}=B,m=Ce(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return dt=t,dt}var mt,Wr;function Ee(){if(Wr)return mt;Wr=1;let e=Re();class o extends e{constructor(d){super(d),this.type="comment"}}return mt=o,o.default=o,mt}var gt,Hr;function Y(){if(Hr)return gt;Hr=1;let{isClean:e,my:o}=Jt(),p=Oe(),d=Ee(),s=Re(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},gt=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},gt}var yt,Gr;function Vt(){if(Gr)return yt;Gr=1;let e=Y(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},yt=d,d.default=d,yt}var wt,Jr;function Ri(){if(Jr)return wt;Jr=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return wt=e,e.default=e,wt}var bt,Vr;function Kt(){if(Vr)return bt;Vr=1;let e=Ri();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return bt=o,o.default=o,bt}var vt,Kr;function As(){if(Kr)return vt;Kr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return vt=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},vt}var xt,Qr;function Qt(){if(Qr)return xt;Qr=1;let e=Y();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return xt=o,o.default=o,e.registerAtRule(o),xt}var St,Yr;function oe(){if(Yr)return St;Yr=1;let e=Y(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},St=d,d.default=d,e.registerRoot(d),St}var Rt,Xr;function Oi(){if(Xr)return Rt;Xr=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Rt=e,e.default=e,Rt}var Ot,Zr;function Yt(){if(Zr)return Ot;Zr=1;let e=Y(),o=Oi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Ot=p,p.default=p,e.registerRule(p),Ot}var Ct,ei;function Ns(){if(ei)return Ct;ei=1;let e=Oe(),o=As(),p=Ee(),d=Qt(),s=oe(),f=Yt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},Pt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),Pt}var At,ii;function Ms(){if(ii)return At;ii=1;let e=Si(),o=Se(),p=Xt();const d=Kt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return At=s,s.default=s,At}var Nt,si;function _s(){if(si)return Nt;si=1;let e=Ms(),o=Ci(),p=Vt(),d=oe();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return Nt=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),Nt}var Mt,ni;function Is(){if(ni)return Mt;ni=1;let e=Oe(),o=xi(),p=Ee(),d=Qt(),s=Ce(),f=oe(),a=Yt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return Mt=m,m.default=m,Mt}var _t,oi;function Ls(){if(oi)return _t;oi=1;let e=Gt(),o=Oe(),p=Ci(),d=Y(),s=_s(),f=Se(),a=Is(),m=Vt(),n=Ri(),h=Ee(),t=Qt(),l=Kt(),i=Ce(),r=Xt(),c=Oi(),u=Yt(),g=oe(),S=Re();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),_t=b,b.default=b,_t}var $s=Ls();const I=Ss($s);I.stringify;I.fromJSON;I.plugin;I.parse;I.list;I.document;I.comment;I.atRule;I.rule;I.decl;I.root;I.CssSyntaxError;I.Declaration;I.Container;I.Processor;I.Document;I.Comment;I.Warning;I.AtRule;I.Result;I.Input;I.Rule;I.Root;I.Node;class Zt{constructor(...o){k(this,"parentElement",null),k(this,"parentNode",null),k(this,"ownerDocument"),k(this,"firstChild",null),k(this,"lastChild",null),k(this,"previousSibling",null),k(this,"nextSibling",null),k(this,"ELEMENT_NODE",1),k(this,"TEXT_NODE",3),k(this,"nodeType"),k(this,"nodeName"),k(this,"RRNodeType")}get childNodes(){const o=[];let p=this.firstChild;for(;p;)o.push(p),p=p.nextSibling;return o}contains(o){if(o instanceof Zt){if(o.ownerDocument!==this.ownerDocument)return!1;if(o===this)return!0}else return!1;for(;o.parentNode;){if(o.parentNode===this)return!0;o=o.parentNode}return!1}appendChild(o){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(o,p){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(o){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const li={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ai={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},pe={};function Ts(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function er(e){if(pe[e])return pe[e];const o=Ts(e)||globalThis[e],p=o.prototype,d=e in li?li[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in ai?ai[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return pe[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?pe[e]=h:p}catch{return p}}const It={};function H(e,o,p){var d;const s=`${e}.${String(p)}`;if(It[s])return It[s].call(o);const f=er(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(It[s]=a,a.call(o)):o[p]}const Lt={};function Ei(e,o,p){const d=`${e}.${String(p)}`;if(Lt[d])return Lt[d].bind(o);const f=er(e)[p];return typeof f!="function"?o[p]:(Lt[d]=f,f.bind(o))}function Us(e){return H("Node",e,"childNodes")}function Ds(e){return H("Node",e,"parentNode")}function ks(e){return H("Node",e,"parentElement")}function qs(e){return H("Node",e,"textContent")}function Fs(e,o){return Ei("Node",e,"contains")(o)}function Bs(e){return Ei("Node",e,"getRootNode")()}function zs(e){return!e||!("host"in e)?null:H("ShadowRoot",e,"host")}function js(e){return e.styleSheets}function Ws(e){return!e||!("shadowRoot"in e)?null:H("Element",e,"shadowRoot")}function Hs(e,o){return H("Element",e,"querySelector")(o)}function Gs(e,o){return H("Element",e,"querySelectorAll")(o)}function Js(){return er("MutationObserver").constructor}const q={childNodes:Us,parentNode:Ds,parentElement:ks,textContent:qs,contains:Fs,getRootNode:Bs,host:zs,styleSheets:js,shadowRoot:Ws,querySelector:Hs,querySelectorAll:Gs,mutationObserver:Js};function Vs(e,o,p=document){const d={capture:!0,passive:!0};return p.addEventListener(e,o,d),()=>p.removeEventListener(e,o,d)}const ee=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let Ut={map:{},getId(){return console.error(ee),-1},getNode(){return console.error(ee),null},removeNodeFromMap(){console.error(ee)},has(){return console.error(ee),!1},reset(){console.error(ee)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(Ut=new Proxy(Ut,{get(e,o,p){return o==="map"&&console.error(ee),Reflect.get(e,o,p)}}));function Ks(e,o,p={}){let d=null,s=0;return function(...f){const a=Date.now();!s&&p.leading===!1&&(s=a);const m=o-(a-s),n=this;m<=0||m>o?(d&&(clearTimeout(d),d=null),s=a,e.apply(n,f)):!d&&p.trailing!==!1&&(d=setTimeout(()=>{s=p.leading===!1?0:Date.now(),d=null,e.apply(n,f)},m))}}function Pi(e,o,p,d,s=window){const f=s.Object.getOwnPropertyDescriptor(e,o);return s.Object.defineProperty(e,o,d?p:{set(a){setTimeout(()=>{p.set.call(this,a)},0),f&&f.set&&f.set.call(this,a)}}),()=>Pi(e,o,f||{},!0)}function Qs(e,o,p){try{if(!(o in e))return()=>{};const d=e[o],s=p(d);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:d}})),e[o]=s,()=>{e[o]=d}}catch{return()=>{}}}let Ai=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(Ai=()=>new Date().getTime());function Ys(e){var o,p,d,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((o=q.parentElement(f.body))==null?void 0:o.scrollLeft)||((p=f?.body)==null?void 0:p.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((d=q.parentElement(f.body))==null?void 0:d.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Xs(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function Zs(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function Ni(e){return e?e.nodeType===e.ELEMENT_NODE?e:q.parentElement(e):null}function en(e,o,p,d){if(!e)return!1;const s=Ni(e);if(!s)return!1;try{if(typeof o=="string"){if(s.classList.contains(o)||d&&s.closest("."+o)!==null)return!0}else if(Tt(s,o,d))return!0}catch{}return!!(p&&(s.matches(p)||d&&s.closest(p)!==null))}function tn(e,o){return o.getId(e)!==-1}function rn(e,o,p){return e.tagName==="TITLE"&&p.headTitleMutations?!0:o.getId(e)===os}function Mi(e,o){if(is(e))return!1;const p=o.getId(e);if(!o.has(p))return!0;const d=q.parentNode(e);return d&&d.nodeType===e.DOCUMENT_NODE?!1:d?Mi(d,o):!0}function sn(e){return!!e.changedTouches}function nn(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function on(e){const o={},p=(s,f)=>{const a={value:s,parent:f,children:[]};return o[s.node.id]=a,a},d=[];for(const s of e){const{nextId:f,parentId:a}=s;if(f&&f in o){const m=o[f];if(m.parent){const n=m.parent.children.indexOf(m);m.parent.children.splice(n,0,p(s,m.parent))}else{const n=d.indexOf(m);d.splice(n,0,p(s,null))}continue}if(a in o){const m=o[a];m.children.push(p(s,m));continue}d.push(p(s,null))}return d}function _i(e,o){o(e.value);for(let p=e.children.length-1;p>=0;p--)_i(e.children[p],o)}function ln(e,o){return!!(e.nodeName==="IFRAME"&&o.getMeta(e))}function an(e,o){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&o.getMeta(e))}function Ii(e,o){var p,d;const s=(d=(p=e.ownerDocument)==null?void 0:p.defaultView)==null?void 0:d.frameElement;if(!s||s===o)return{x:0,y:0,relativeScale:1,absoluteScale:1};const f=s.getBoundingClientRect(),a=Ii(s,o),m=f.height/s.clientHeight;return{x:f.x*a.relativeScale+a.x,y:f.y*a.relativeScale+a.y,relativeScale:m,absoluteScale:a.absoluteScale*m}}function un(e){return e?e instanceof Zt&&"shadowRoot"in e?!!e.shadowRoot:!!q.shadowRoot(e):!1}function Li(e,o){const p=e[o[0]];return o.length===1?p:Li(p.cssRules[o[1]].cssRules,o.slice(2))}function fn(e){const o=[...e],p=o.pop();return{positions:o,index:p}}function hn(e){const o=new Set,p=[];for(let d=e.length;d--;){const s=e[d];o.has(s.id)||(p.push(s),o.add(s.id))}return p}class cn{constructor(){_e(this,"id",1),_e(this,"styleIDMap",new WeakMap),_e(this,"idStyleMap",new Map)}getId(o){return this.styleIDMap.get(o)??-1}has(o){return this.styleIDMap.has(o)}add(o,p){if(this.has(o))return this.getId(o);let d;return p===void 0?d=this.id++:d=p,this.styleIDMap.set(o,d),this.idStyleMap.set(d,o),d}getStyle(o){return this.idStyleMap.get(o)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function $i(e){var o;let p=null;return"getRootNode"in e&&((o=q.getRootNode(e))==null?void 0:o.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&q.host(q.getRootNode(e))&&(p=q.host(q.getRootNode(e))),p}function Ti(e){let o=e,p;for(;p=$i(o);)o=p;return o}function Ui(e){const o=e.ownerDocument;if(!o)return!1;const p=Ti(e);return q.contains(o,p)}function pn(e){const o=e.ownerDocument;return o?q.contains(o,e)||Ui(e):!1}const dn=Object.freeze(Object.defineProperty({__proto__:null,StyleSheetMirror:cn,get _mirror(){return Ut},closestElementOfNode:Ni,getBaseDimension:Ii,getNestedRule:Li,getPositionsAndIndex:fn,getRootShadowHost:Ti,getShadowHost:$i,getWindowHeight:Xs,getWindowScroll:Ys,getWindowWidth:Zs,hasShadowRoot:un,hookSetter:Pi,inDom:pn,isAncestorRemoved:Mi,isBlocked:en,isIgnored:rn,isSerialized:tn,isSerializedIframe:ln,isSerializedStylesheet:an,iterateResolveTree:_i,legacy_isTouchEvent:sn,get nowTimestamp(){return Ai},on:Vs,patch:Qs,polyfill:nn,queueToResolveTrees:on,shadowHostInDom:Ui,throttle:Ks,uniqueTextMutations:hn},Symbol.toStringTag,{value:"Module"}));var ui="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",mn=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var de=0;de> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`;typeof self<"u"&&self.Blob&&new Blob([gn],{type:"text/javascript;charset=utf-8"});try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((tr=e.contentWindow)==null?void 0:tr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}ns();var fi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(fi||(fi={}));class X{constructor(o){le(this,"fileName"),le(this,"functionName"),le(this,"lineNumber"),le(this,"columnNumber"),this.fileName=o.fileName||"",this.functionName=o.functionName||"",this.lineNumber=o.lineNumber,this.columnNumber=o.columnNumber}toString(){const o=this.lineNumber||"",p=this.columnNumber||"";return this.functionName?`${this.functionName} (${this.fileName}:${o}:${p})`:`${this.fileName}:${o}:${p}`}}const yn=/(^|@)\S+:\d+/,hi=/^\s*at .*(\S+:\d+|\(native\))/m,wn=/^(eval@)?(\[native code])?$/,$t={parse:function(e){return e?typeof e.stacktrace<"u"||typeof e["opera#sourceloc"]<"u"?this.parseOpera(e):e.stack&&e.stack.match(hi)?this.parseV8OrIE(e):e.stack?this.parseFFOrSafari(e):(console.warn("[console-record-plugin]: Failed to parse error object:",e),[]):[]},extractLocation:function(e){if(e.indexOf(":")===-1)return[e];const p=/(.+?)(?::(\d+))?(?::(\d+))?$/.exec(e.replace(/[()]/g,""));if(!p)throw new Error(`Cannot parse given url: ${e}`);return[p[1],p[2]||void 0,p[3]||void 0]},parseV8OrIE:function(e){return e.stack.split(` -`).filter(function(p){return!!p.match(hi)},this).map(function(p){p.indexOf("(eval ")>-1&&(p=p.replace(/eval code/g,"eval").replace(/(\(eval at [^()]*)|(\),.*$)/g,""));let d=p.replace(/^\s+/,"").replace(/\(eval code/g,"(");const s=d.match(/ (\((.+):(\d+):(\d+)\)$)/);d=s?d.replace(s[0],""):d;const f=d.split(/\s+/).slice(1),a=this.extractLocation(s?s[1]:f.pop()),m=f.join(" ")||void 0,n=["eval",""].indexOf(a[0])>-1?void 0:a[0];return new X({functionName:m,fileName:n,lineNumber:a[1],columnNumber:a[2]})},this)},parseFFOrSafari:function(e){return e.stack.split(` -`).filter(function(p){return!p.match(wn)},this).map(function(p){if(p.indexOf(" > eval")>-1&&(p=p.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,":$1")),p.indexOf("@")===-1&&p.indexOf(":")===-1)return new X({functionName:p});{const d=/((.*".+"[^@]*)?[^@]*)(?:@)/,s=p.match(d),f=s&&s[1]?s[1]:void 0,a=this.extractLocation(p.replace(d,""));return new X({functionName:f,fileName:a[0],lineNumber:a[1],columnNumber:a[2]})}},this)},parseOpera:function(e){return!e.stacktrace||e.message.indexOf(` -`)>-1&&e.message.split(` -`).length>e.stacktrace.split(` -`).length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(e){const o=/Line (\d+).*script (?:in )?(\S+)/i,p=e.message.split(` -`),d=[];for(let s=2,f=p.length;s/,"$2").replace(/\([^)]*\)/g,"")||void 0;return new X({functionName:a,fileName:s[0],lineNumber:s[1],columnNumber:s[2]})},this)}};function bn(e){if(!e||!e.outerHTML)return"";let o="";for(;e.parentElement;){let p=e.localName;if(!p)break;p=p.toLowerCase();const d=e.parentElement,s=[];if(d.children&&d.children.length>0)for(let f=0;f1&&(p+=`:eq(${s.indexOf(e)})`),o=p+(o?">"+o:""),e=d}return o}function Dt(e){return Object.prototype.toString.call(e)==="[object Object]"}function Di(e,o){if(o===0)return!0;const p=Object.keys(e);for(const d of p)if(Dt(e[d])&&Di(e[d],o-1))return!0;return!1}function Z(e,o){const p={numOfKeysLimit:50,depthOfLimit:4};Object.assign(p,o);const d=[],s=[];return JSON.stringify(e,function(m,n){if(d.length>0){const h=d.indexOf(this);~h?d.splice(h+1):d.push(this),~h?s.splice(h,1/0,m):s.push(m),~d.indexOf(n)&&(d[0]===n?n="[Circular ~]":n="[Circular ~."+s.slice(0,d.indexOf(n)).join(".")+"]")}else d.push(n);if(n===null)return n;if(n===void 0)return"undefined";if(f(n))return a(n);if(typeof n=="bigint")return n.toString()+"n";if(n instanceof Event){const h={};for(const t in n){const l=n[t];Array.isArray(l)?h[t]=bn(l.length?l[0]:null):h[t]=l}return h}else{if(n instanceof Node)return n instanceof HTMLElement?n?n.outerHTML:"":n.nodeName;if(n instanceof Error)return n.stack?n.stack+` -End of stack for Error object`:n.name+": "+n.message}return n});function f(m){return!!(Dt(m)&&Object.keys(m).length>p.numOfKeysLimit||typeof m=="function"||Dt(m)&&Di(m,p.depthOfLimit))}function a(m){let n=m.toString();return p.stringLengthLimit&&n.length>p.stringLengthLimit&&(n=`${n.slice(0,p.stringLengthLimit)}...`),n}}const ci={level:["assert","clear","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],lengthThreshold:1e3,logger:"console"};function vn(e,o,p){const d=p?Object.assign({},ci,p):ci,s=d.logger;if(!s)return()=>{};let f;typeof s=="string"?f=o[s]:f=s;let a=0,m=!1;const n=[];if(d.level.includes("error")){const t=i=>{const r=i.message,c=i.error,u=$t.parse(c).map(S=>S.toString()),g=[Z(r,d.stringifyOptions)];e({level:"error",trace:u,payload:g})};o.addEventListener("error",t),n.push(()=>{o.removeEventListener("error",t)});const l=i=>{let r,c;i.reason instanceof Error?(r=i.reason,c=[Z(`Uncaught (in promise) ${r.name}: ${r.message}`,d.stringifyOptions)]):(r=new Error,c=[Z("Uncaught (in promise)",d.stringifyOptions),Z(i.reason,d.stringifyOptions)]);const u=$t.parse(r).map(g=>g.toString());e({level:"error",trace:u,payload:c})};o.addEventListener("unhandledrejection",l),n.push(()=>{o.removeEventListener("unhandledrejection",l)})}for(const t of d.level)n.push(h(f,t));return()=>{n.forEach(t=>t())};function h(t,l){return t[l]?dn.patch(t,l,i=>(...r)=>{if(i.apply(this,r),!(l==="assert"&&r[0])&&!m){m=!0;try{const c=$t.parse(new Error).map(S=>S.toString()).splice(1),g=(l==="assert"?r.slice(1):r).map(S=>Z(S,d.stringifyOptions));a++,a{}}}const xn="rrweb/console@1",Sn=e=>({name:xn,observer:vn,options:e});export{xn as PLUGIN_NAME,Sn as getRecordConsolePlugin}; - -``` - ---- - -## .output/public/robots.txt - -```txt -User-Agent: * -Disallow: - -``` - ---- - -## .output/server/chunks/_/error-500.mjs - -```mjs -import { escapeHtml } from '@vue/shared'; - -const _messages = { - "appName": "Nuxt", - "status": 500, - "statusText": "Internal server error", - "description": "This page is temporarily unavailable.", - "refresh": "Refresh this page" -}; -const template = (messages) => { - messages = { - ..._messages, - ...messages - }; - return "" + escapeHtml(messages.status) + " - " + escapeHtml(messages.statusText) + " | " + escapeHtml(messages.appName) + " - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-nuxt-4/references/browser-sdk-2.md b/skills/integration/integration-nuxt-4/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-nuxt-4/references/browser-sdk-2.md +++ b/skills/integration/integration-nuxt-4/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-nuxt-4/references/browser-unified-sdk.md b/skills/integration/integration-nuxt-4/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-nuxt-4/references/browser-unified-sdk.md +++ b/skills/integration/integration-nuxt-4/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-python/SKILL.md b/skills/integration/integration-python/SKILL.md index b7cdabf53..52998efd7 100644 --- a/skills/integration/integration-python/SKILL.md +++ b/skills/integration/integration-python/SKILL.md @@ -16,15 +16,15 @@ This skill helps you add Amplitude analytics to Python applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Python example project code -- `references/python.md` - Amplitude documentation for Python +- `references/python.md` - Python - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-python/references/amplitude-quickstart.md b/skills/integration/integration-python/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-python/references/amplitude-quickstart.md +++ b/skills/integration/integration-python/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-python/references/python.md b/skills/integration/integration-python/references/python.md index 324f2e306..5efc98b78 100644 --- a/skills/integration/integration-python/references/python.md +++ b/skills/integration/integration-python/references/python.md @@ -1,1424 +1,43 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - Python | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[Python](/docs/sdks/analytics/python) -
- -
-
-
-
- -
-
-
-

Python

- -
-
- -
- current - -

- - - - Python SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/python.svg) Python SDK](/docs/sdks/analytics-sdks/python/python-sdk) - - - - - - - - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-Python) +- [Releases](https://github.com/amplitude/Amplitude-Python/releases) +- [Ampli](/docs/sdks/analytics-sdks/python/ampli-for-python-sdk) \ No newline at end of file diff --git a/skills/integration/integration-react-native/SKILL.md b/skills/integration/integration-react-native/SKILL.md index 9d3a404d1..c93dccefb 100644 --- a/skills/integration/integration-react-native/SKILL.md +++ b/skills/integration/integration-react-native/SKILL.md @@ -16,10 +16,10 @@ This skill helps you add Amplitude analytics to React Native applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files diff --git a/skills/integration/integration-react-native/references/amplitude-quickstart.md b/skills/integration/integration-react-native/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-native/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-native/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-native/references/react-native-sdk.md b/skills/integration/integration-react-native/references/react-native-sdk.md index 78e53798b..2e257498c 100644 --- a/skills/integration/integration-react-native/references/react-native-sdk.md +++ b/skills/integration/integration-react-native/references/react-native-sdk.md @@ -1,1235 +1,60 @@ - +The React Native SDK lets you send events to Amplitude. - - - - - - +## React Native support - - - - - - - - - React Native SDK | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Gradle +Android Gradle Plugin -
-
+\>= 1.4.0 -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

React Native SDK

-
-
- - - - - - - - -

The React Native SDK lets you send events to Amplitude.

-

-

React Native support

-Because React-Native doesn't provide stable release versioning, ensuring backward compatibility is challenging. Additionally, React-Native itself isn't backward compatible and may introduce breaking changes across different versions. Check the React-Native compatibility list for more details. Amplitude supports only the latest version of React-Native.
-

-

Compatibility matrix

-

The following matrix lists the support for Amplitude React Native SDK version for different versions of React Native and React Native CLI.

- - - - - - - - - - - - - - - - - - - - - - - -
@amplitude/analytics-react-nativereact-nativeGradleAndroid Gradle Plugin
>= 1.4.0>= 0.687.5.1+7.2.1+
>= 1.0.0, <= 1.3.6>= 0.61, <= 0.703.5.3+3.5.3+
-

Learn more about the Android Gradle Plugin compatibility.

-

Install the SDK

-

To get started with using Amplitude React Native SDK, install the package to your project with npm. You must also install @react-native-async-storage/async-storage for the SDK to work as expected.

-

-

Web and Expo support

-This SDK can be used for react-native apps built for web or built using Expo (Expo Go not yet supported).
-

-

-

-
-
- -

-
npm install @amplitude/analytics-react-native
-npm install @react-native-async-storage/async-storage
-
-

-

-

-
yarn add @amplitude/analytics-react-native
-yarn add @react-native-async-storage/async-storage
-
-

-

-

-
expo install @amplitude/analytics-react-native
-expo install @react-native-async-storage/async-storage
-
-

-

-

-

Install the native modules to run the SDK on iOS.

-
cd ios
+\>= 0.68
+
+7.5.1+
+
+7.2.1+
+
+\>= 1.0.0, <= 1.3.6
+
+\>= 0.61, <= 0.70
+
+3.5.3+
+
+3.5.3+
+
+Learn more about the Android [Gradle Plugin compatibility](https://developer.android.com/studio/releases/gradle-plugin#updating-gradle).
+
+## Install the SDK[](#install-the-sdk "Permalink")
+
+To get started with using Amplitude React Native SDK, install the package to your project with npm. You must also install `@react-native-async-storage/async-storage` for the SDK to work as expected.
+
+## Web and Expo support
+
+This SDK can be used for react-native apps built for web or built using [Expo](https://expo.dev/) (Expo Go not yet supported).
+
+Install the native modules to run the SDK on iOS.
+
+```bash
+cd ios
 pod install
-
-

Initialize the SDK

-

Initialization is necessary before any instrumentation is done. The API key for your Amplitude project is required. Optionally, a user ID and config object can be passed in this call. The SDK can be used anywhere after it's initialized anywhere in an application.

-
import { init } from '@amplitude/analytics-react-native';
+```
+
+## Initialize the SDK[](#initialize-the-sdk "Permalink")
+
+Initialization is necessary before any instrumentation is done. The API key for your Amplitude project is required. Optionally, a user ID and config object can be passed in this call. The SDK can be used anywhere after it's initialized anywhere in an application.
+
+```ts
+import { init } from '@amplitude/analytics-react-native';
 
 // Option 1, initialize with API_KEY only
 init(API_KEY);
@@ -1241,172 +66,20 @@ init(API_KEY, 'user@amplitude.com');
 init(API_KEY, 'user@amplitude.com', {
   disableCookies: true, // Disables the use of browser cookies
 });
-
-

Configure the SDK

-

-

Web vs. mobile

-The configuration of the SDK is shared across web and mobile platforms. However, many of these options simply don't apply when running the SDK on native platforms (for example iOS, Android). For example, when the SDK is run on web, the identity is stored in the browser cookie by default, whereas on native platforms identity is stored in async storage.
-

-

- Configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events that are batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to retryable errors.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level.LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class from the Logger to emit log messages to desired destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
deviceIdstring. Sets an identifier for the device running your application.UUID()
cookieExpirationnumber. Sets expiration of cookies created in days.365 days
cookieSameSitestring. Sets SameSite property of cookies created.Lax
cookieSecureboolean. Sets Secure property of cookies created.false
cookieStorageStorage<UserSession>. Sets a custom implementation of Storage<UserSession> to persist user identity.MemoryStorage<UserSession>
cookieUpgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
disableCookiesboolean. Sets permission to use cookies. If value is true, localStorage API is used to persist user identity.The cookies is enable by default.
domainstring. Sets the domain property of cookies created.undefined
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
userIdnumber. Sets an identifier for the user being tracked. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of additional properties. Please refer to Optional tracking section for more information.Enable all tracking options by default.
storageProviderStorage<Event[]>. Implements a custom storageProvider class from Storage.MemoryStorage
trackingSessionEventsboolean. Whether to automatically log start and end session events corresponding to the start and end of a user's session.false
migrateLegacyDataboolean. Available in 1.3.4+. Whether to migrate maintenance SDK data (events, user/device ID).true
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. Every event logged by the track method is queued in memory. Events are flushed in batches in background. You can customize batch behavior with flushQueueSize and flushIntervalMillis. By default, the serverUrl will be https://api2.amplitude.com/2/httpapi. For customers who want to send large batches of data at a time, set useBatch to true to set setServerUrl to batch event upload API https://api2.amplitude.com/batch. Both the regular mode and the batch mode use the same events upload threshold and flush time intervals.

-
import * as amplitude from '@amplitude/analytics-react-native';
+```
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+## Web vs. mobile
+
+The configuration of the SDK is shared across web and mobile platforms. However, many of these options simply don't apply when running the SDK on native platforms (for example iOS, Android). For example, when the SDK is run on web, the identity is stored in the browser cookie by default, whereas on native platforms identity is stored in async storage.
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. Every event logged by the `track` method is queued in memory. Events are flushed in batches in background. You can customize batch behavior with `flushQueueSize` and `flushIntervalMillis`. By default, the serverUrl will be `https://api2.amplitude.com/2/httpapi`. For customers who want to send large batches of data at a time, set `useBatch` to `true` to set `setServerUrl` to batch event upload API `https://api2.amplitude.com/batch`. Both the regular mode and the batch mode use the same events upload threshold and flush time intervals.
+
+```ts
+import * as amplitude from '@amplitude/analytics-react-native';
 
 amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   // Events queued in memory will flush when number of events exceed upload threshold
@@ -1416,60 +89,81 @@ amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   // Default value is 10000 milliseconds
   flushIntervalMillis: 20000,
 });
-
-

EU data residency

-

You can configure the server zone when initializing the client for sending data to Amplitude's EU servers. The SDK sends data based on the server zone if it's set.

-

-

Note

-For EU data residency, the project must be set up inside Amplitude EU. You must initialize the SDK with the API key from Amplitude EU.
-

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+You can configure the server zone when initializing the client for sending data to Amplitude's EU servers. The SDK sends data based on the server zone if it's set.
+
+## Note
+
+For EU data residency, the project must be set up inside Amplitude EU. You must initialize the SDK with the API key from Amplitude EU.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   serverZone: 'EU',
 });
-
-

Debugging

-

You can control the level of logs printed to the developer console.

-
    -
  • 'None': Suppresses all log messages.
  • -
  • 'Error': Shows error messages only.
  • -
  • 'Warn': Shows error messages and warnings. This is the default value if logLevel isn't explicitly specified.
  • -
  • 'Verbose': Shows informative messages.
  • -
  • 'Debug': Shows error messages, warnings, and informative messages that may be useful for debugging, including the function context information for all SDK public method invocations. This logging mode is only suggested to be used in development phases.
  • -
-

Set the log level by configuring the logLevel with the level you want.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Debugging[](#debugging "Permalink")
+
+You can control the level of logs printed to the developer console.
+
+-   'None': Suppresses all log messages.
+-   'Error': Shows error messages only.
+-   'Warn': Shows error messages and warnings. This is the default value if `logLevel` isn't explicitly specified.
+-   'Verbose': Shows informative messages.
+-   'Debug': Shows error messages, warnings, and informative messages that may be useful for debugging, including the function context information for all SDK public method invocations. This logging mode is only suggested to be used in development phases.
+
+Set the log level by configuring the `logLevel` with the level you want.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

The default logger outputs extra function context information to the developer console when invoking any SDK public method, including:

-
    -
  • 'type': Category of this context, for example "invoke public method".
  • -
  • 'name': Name of invoked function, for example "track".
  • -
  • 'args': Arguments of the invoked function.
  • -
  • 'stacktrace': Stacktrace of the invoked function.
  • -
  • 'time': Start and end timestamp of the function invocation.
  • -
  • 'states': Useful internal states snapshot before and after the function invocation.
  • -
-

Track events

-

-

Note

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, the upload may be rejected with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

Events represent how users interact with your application. For example, "Button Clicked" may be an action you want to note.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+The default logger outputs extra function context information to the developer console when invoking any SDK public method, including:
+
+-   'type': Category of this context, for example "invoke public method".
+-   'name': Name of invoked function, for example "track".
+-   'args': Arguments of the invoked function.
+-   'stacktrace': Stacktrace of the invoked function.
+-   'time': Start and end timestamp of the function invocation.
+-   'states': Useful internal states snapshot before and after the function invocation.
+
+## Track events[](#track-events "Permalink")
+
+## Note
+
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, the upload may be rejected with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+Events represent how users interact with your application. For example, "Button Clicked" may be an action you want to note.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 // Track a basic event
 track('Button Clicked');
@@ -1479,10 +173,14 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 track('Button Clicked', eventProperties);
-
-

Track events to multiple projects

-

If you need to log events to multiple Amplitude projects, you'll need to create separate instances for each Amplitude project. Then, pass the instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKeys, userIds, deviceIds, and settings.

-
import * as amplitude from '@amplitude/analytics-react-native';
+```
+
+### Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+If you need to log events to multiple Amplitude projects, you'll need to create separate instances for each Amplitude project. Then, pass the instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKeys`, `userIds`, `deviceIds`, and settings.
+
+```ts
+import * as amplitude from '@amplitude/analytics-react-native';
 
 const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
@@ -1491,138 +189,200 @@ const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties help you understand your users at the time they performed some action within your app such as their device details, their preferences, or language.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. The operations are declared through a provided Identify interface. You can chain multiple operations together in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Note

-If the Identify call is sent after the event, the results of operations will be visible immediately in the dashboard user’s profile area, but it won't appear in chart result until another event is sent after the Identify call. The identify call only affects events going forward. More details here.
-

-

Identify

-

The Identify object provides controls over setting user properties. An Identify object must first be instantiated, then Identify methods can be called on it, and finally the client will make a call with the Identify object.

-
import { identify, Identify } from '@amplitude/analytics-react-native';
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties help you understand your users at the time they performed some action within your app such as their device details, their preferences, or language.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. The operations are declared through a provided Identify interface. You can chain multiple operations together in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Note
+
+If the Identify call is sent after the event, the results of operations will be visible immediately in the dashboard user’s profile area, but it won't appear in chart result until another event is sent after the Identify call. The identify call only affects events going forward. More details [here](/docs/data/user-properties-and-events).
+
+### Identify[](#identify "Permalink")
+
+The Identify object provides controls over setting user properties. An Identify object must first be instantiated, then Identify methods can be called on it, and finally the client will make a call with the Identify object.
+
+```ts
+import { identify, Identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identify(identifyObj);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.set('location', 'LAX');
 
 identify(identifyObj);
-
-

Identify.setOnce

-

This method sets the value of a user property only once. Subsequent calls using setOnce() will be ignored. For example, you can set an initial login method for a user and since only the initial value is tracked, setOnce() ignores subsequent calls.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only once. Subsequent calls using setOnce() will be ignored. For example, you can set an initial login method for a user and since only the initial value is tracked, setOnce() ignores subsequent calls.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.setOnce('initial-location', 'SFO');
 
 identify(identifyObj);
-
-

Identify.add

-

This method increments a user property by some numerical value. If the user property doesn't have a value set yet, it will be initialized to 0 before being incremented. For example, you can track a user's travel count.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by some numerical value. If the user property doesn't have a value set yet, it will be initialized to 0 before being incremented. For example, you can track a user's travel count.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.add('travel-count', 1);
 
 identify(identifyObj);
-
-

Arrays in user properties

-

Arrays can be used as user properties. You can directly set arrays or use prepend, append, preInsert and postInsert to generate an array.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Arrays can be used as user properties. You can directly set arrays or use `prepend`, `append`, `preInsert` and `postInsert` to generate an array.
+
+#### `Identify.prepend`[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.prepend('visited-locations', 'LAX');
 
 identify(identifyObj);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### `Identify.append`[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are prepended.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.append('visited-locations', 'SFO');
 
 identify(identifyObj);
-
-

Identify.preInsert

-

This method pre-inserts a value or values to a user property if it doesn't exist in the user property yet. Pre-insert means inserting the value at the beginning of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are pre-inserted. If the user property has an existing value, it will be no operation.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### `Identify.preInsert`[](#identifypreinsert "Permalink")
+
+This method pre-inserts a value or values to a user property if it doesn't exist in the user property yet. Pre-insert means inserting the value at the beginning of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are pre-inserted. If the user property has an existing value, it will be no operation.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.preInsert('unique-locations', 'LAX');
 
 identify(identifyObj);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the value at the end of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are post-inserted. If the user property has an existing value, it will be no operation.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the value at the end of a given list. If the user property doesn't have a value set yet, it will be initialized to an empty list before the new values are post-inserted. If the user property has an existing value, it will be no operation.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.postInsert('unique-locations', 'SFO');
 
 identify(identifyObj);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the item doesn't exist in the user property, it's a no-op.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the item doesn't exist in the user property, it's a no-op.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.remove('unique-locations', 'JFK')
 
 identify(identifyObj);
-
-

Identify.clearAll

-

This method removes all user properties from a user. Use clearAll with care because the operation is irreversible.

-
import { Identify, identify } from '@amplitude/analytics-react-native';
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from a user. Use `clearAll` with care because the operation is irreversible.
+
+```ts
+import { Identify, identify } from '@amplitude/analytics-react-native';
 
 const identifyObj = new Identify();
 identifyObj.clearAll();
 
 identify(identifyObj);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to indicate that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName would be '15'.

-
import { setGroup } from '@amplitude/analytics-react-native';
+```
+
+### User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's groupType, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to indicate that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` would be '15'.
+
+```ts
+import { setGroup } from '@amplitude/analytics-react-native';
 
 // set group with single group name
 setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'tennis' and 'soccer', then the groupName would be '["tennis", "soccer"]'.

-
import { setGroup } from '@amplitude/analytics-react-native';
+```
+
+If Joe is in 'sport' 'tennis' and 'soccer', then the `groupName` would be '\["tennis", "soccer"\]'.
+
+```ts
+import { setGroup } from '@amplitude/analytics-react-native';
 
 // set group with multiple group names
 setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

You can also set event-level groups by passing an Event Object with groups to track. With event-level groups, the group designation applies only to the specific event being logged, and doesn't persist on the user unless you explicitly set it with setGroup.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+You can also set **event-level groups** by passing an `Event` Object with `groups` to `track`. With event-level groups, the group designation applies only to the specific event being logged, and doesn't persist on the user unless you explicitly set it with `setGroup`.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 });
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that will be applied to the group.

-
import { Identify, groupIdentify } from '@amplitude/analytics-react-native';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that will be applied to the group.
+
+```ts
+import { Identify, groupIdentify } from '@amplitude/analytics-react-native';
 
 const groupType = 'plan';
 const groupName = 'enterprise';
@@ -1630,11 +390,16 @@ const event = new Identify()
 event.set('key1', 'value1');
 
 groupIdentify(groupType, groupName, identify);
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances will store each revenue transaction and allow you to define several special revenue properties (such as "revenueType", "productIdentifier", etc.) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

To track revenue from a user, call revenue each time a user generates revenue. For example, 3 units of a product were purchased at $3.99.

-
import { Revenue, revenue } from '@amplitude/analytics-react-native';
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances will store each revenue transaction and allow you to define several special revenue properties (such as "revenueType", "productIdentifier", etc.) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+To track revenue from a user, call revenue each time a user generates revenue. For example, 3 units of a product were purchased at $3.99.
+
+```ts
+import { Revenue, revenue } from '@amplitude/analytics-react-native';
 
 const event = new Revenue()
   .setProductId('com.company.productId')
@@ -1642,156 +407,186 @@ const event = new Revenue()
   .setQuantity(3);
 
 revenue(event);
-
-

Revenue interface

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
product_idOptional. String. An identifier for the product. Amplitude recommends something like the Google Play Store product ID. Defaults to null.
quantityRequired. Int. The quantity of products purchased. revenue = quantity * price. Defaults to 1
priceRequired. Double. The price of the products purchased, and this can be negative. revenue = quantity * price. Defaults to null.
revenue_typeOptional, but required for revenue verification. String. The revenue type (for example tax, refund, income). Defaults to null.
receiptOptional. String. The receipt identifier of the revenue. Defaults to null
receipt_sigOptional, but required for revenue verification. String. The receipt signature of the revenue. Defaults to null.
propertiesOptional. JSONObject. An object of event properties to include in the revenue event. Defaults to null.
-

Flush the event buffer

-

The flush method triggers the client to send buffered events.

-
import { flush } from '@amplitude/analytics-react-native';
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Name
+
+Description
+
+`product_id`
+
+Optional. String. An identifier for the product. Amplitude recommends something like the Google Play Store product ID. Defaults to null.
+
+`quantity`
+
+Required. Int. The quantity of products purchased. `revenue = quantity * price`. Defaults to 1
+
+`price`
+
+Required. Double. The price of the products purchased, and this can be negative. `revenue = quantity * price`. Defaults to null.
+
+`revenue_type`
+
+Optional, but required for revenue verification. String. The revenue type (for example tax, refund, income). Defaults to null.
+
+`receipt`
+
+Optional. String. The receipt identifier of the revenue. Defaults to null
+
+`receipt_sig`
+
+Optional, but required for revenue verification. String. The receipt signature of the revenue. Defaults to null.
+
+`properties`
+
+Optional. JSONObject. An object of event properties to include in the revenue event. Defaults to null.
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events.
+
+```typescript
+import { flush } from '@amplitude/analytics-react-native';
 
 flush();
-
-

By default, flush is called automatically in an interval, if you want to flush the events altogether, you can control the async flow with the optional Promise interface, for example:

-
await init(AMPLITUDE_API_KEY).promise;
+```
+
+By default, `flush` is called automatically in an interval, if you want to flush the events altogether, you can control the async flow with the optional Promise interface, for example:
+
+```typescript
+await init(AMPLITUDE_API_KEY).promise;
 track('Button Clicked');
 await flush().promise;
-
-

Custom user ID

-

If your app has its login system that you want to track users with, you can call setUserId at any time.

-

TypeScript

-
import { setUserId } from '@amplitude/analytics-react-native';
+```
+
+## Custom user ID[](#custom-user-id "Permalink")
+
+If your app has its login system that you want to track users with, you can call `setUserId` at any time.
+
+TypeScript
+
+```ts
+import { setUserId } from '@amplitude/analytics-react-native';
 
 setUserId('user@amplitude.com');
-
-

You can also assign the User ID as an argument to the init call.

-
import { init } from '@amplitude/analytics-react-native';
+```
+
+You can also assign the User ID as an argument to the init call.
+
+```ts
+import { init } from '@amplitude/analytics-react-native';
 
 init(API_KEY, 'user@amplitude.com');
-
-

Custom session ID

-

You can assign a new Session ID using setSessionId. When setting a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-

TypeScript

-
import { setSessionId } from '@amplitude/analytics-react-native';
+```
+
+## Custom session ID[](#custom-session-id "Permalink")
+
+You can assign a new Session ID using `setSessionId`. When setting a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+TypeScript
+
+```ts
+import { setSessionId } from '@amplitude/analytics-react-native';
 
 setSessionId(Date.now());
-
-

Custom device ID

-

If your app has its login system that you want to track users with, you can call setUserId at any time.

-

You can assign a new device ID using deviceId. When setting a custom device ID, make sure the value is sufficiently unique. A UUID is recommended.

-
import { setDeviceId } from '@amplitude/analytics-react-native';
+```
+
+## Custom device ID[](#custom-device-id "Permalink")
+
+If your app has its login system that you want to track users with, you can call `setUserId` at any time.
+
+You can assign a new device ID using `deviceId`. When setting a custom device ID, make sure the value is sufficiently unique. A UUID is recommended.
+
+```ts
+import { setDeviceId } from '@amplitude/analytics-react-native';
 const { uuid } = require('uuidv4');
 
 setDeviceId(uuid());
-
-

Reset when a user logs out

-

reset is a shortcut to anonymize users after they log out, by:

-
    -
  • setting userId to undefined
  • -
  • setting deviceId to a new UUID value
  • -
-

With an undefined userId and a completely new deviceId, the current user would appear as a brand new user in dashboard.

-
import { reset } from '@amplitude/analytics-react-native';
+```
+
+## Reset when a user logs out[](#reset-when-a-user-logs-out "Permalink")
+
+`reset` is a shortcut to anonymize users after they log out, by:
+
+-   setting `userId` to `undefined`
+-   setting `deviceId` to a new UUID value
+
+With an undefined `userId` and a completely new `deviceId`, the current user would appear as a brand new user in dashboard.
+
+```ts
+import { reset } from '@amplitude/analytics-react-native';
 
 reset();
-
-

Opt users out of tracking

-

You can turn off logging for a given user by setting setOptOut to true.

-
import { setOptOut } from '@amplitude/analytics-react-native';
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+You can turn off logging for a given user by setting `setOptOut` to `true`.
+
+```ts
+import { setOptOut } from '@amplitude/analytics-react-native';
 
 setOptOut(true);
-
-

No events are saved or sent to the server while setOptOut is enabled, and the setting persists across page loads.

-

Re-enable logging by setting setOptOut to false.

-
import { setOptOut } from '@amplitude/analytics-react-native';
+```
+
+No events are saved or sent to the server while `setOptOut` is enabled, and the setting persists across page loads.
+
+Re-enable logging by setting `setOptOut` to `false`.
+
+```ts
+import { setOptOut } from '@amplitude/analytics-react-native';
 
 setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
adidtrue
carriertrue
deviceManufacturertrue
deviceModeltrue
ipAddresstrue
languagetrue
osNametrue
osVersiontrue
platformtrue
-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`adid`
+
+`true`
+
+`carrier`
+
+`true`
+
+`deviceManufacturer`
+
+`true`
+
+`deviceModel`
+
+`true`
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
+
+`osName`
+
+`true`
+
+`osVersion`
+
+`true`
+
+`platform`
+
+`true`
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   trackingOptions: {
     adid: false,
     appSetId: false,
@@ -1806,46 +601,69 @@ setOptOut(false);
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-
import { track } from '@amplitude/analytics-react-native';
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+```ts
+import { track } from '@amplitude/analytics-react-native';
 
 // Using async/await
 const results = await track('Button Clicked').promise;
 result.event; // {...} (The final event object sent to Amplitude)
 result.code; // 200 (The HTTP response status code of the request.
-result.message; // "Event tracked successfully" (The response message)
+result.message; // "Event tracked successfully" (The response message)
 
 // Using promises
-track('Button Clicked').promise.then((result) => {
+track('Button Clicked').promise.then((result) => {
   result.event; // {...} (The final event object sent to Amplitude)
   result.code; // 200 (The HTTP response status code of the request.
-  result.message; // "Event tracked successfully" (The response message)
+  result.message; // "Event tracked successfully" (The response message)
 });
-
-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment type) or sending to third-party APIs (destination type). A plugin is an object with methods setup() and execute().

-

For Session Replay integration with Segment, review the Session Replay React Native Segment Integration guide.

-

add

-

The add method adds a plugin to Amplitude. Plugins can help processing and sending events.

-
import { add } from '@amplitude/analytics-react-native';
+```
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment type) or sending to third-party APIs (destination type). A plugin is an object with methods `setup()` and `execute()`.
+
+For Session Replay integration with Segment, review the [Session Replay React Native Segment Integration](/docs/session-replay/session-replay-react-native-segment-integration) guide.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude. Plugins can help processing and sending events.
+
+```typescript
+import { add } from '@amplitude/analytics-react-native';
 
 add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
import { remove } from '@amplitude/analytics-react-native';
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```typescript
+import { remove } from '@amplitude/analytics-react-native';
 
 remove(plugin.name);
-
-

Plugin setup

-

This method contains logic for preparing the plugin for use and has config as a parameter. The expected return value is undefined. A typical use for this method, is to copy configuration from config or instantiate plugin dependencies. This method is called when the plugin is registered to the client via client.add().

-

Plugin.execute

-

This method contains the logic for processing events and has event as parameter. If used as enrichment type plugin, the expected return value is the modified/enriched event; while if used as a destination type plugin, the expected return value is a map with keys: event (BaseEvent), code (number), and message (string). This method is called for each event, including Identify, GroupIdentify and Revenue events, that's instrumented using the client interface.

-

Enrichment type plugin example

-

Here's an example of a plugin that modifies each event that's instrumented by adding an increment integer to event_id property of an event starting from 100.

-
import { init, add } from '@amplitude/analytics-react-native';
+```
+
+### Plugin setup[](#plugin-setup "Permalink")
+
+This method contains logic for preparing the plugin for use and has config as a parameter. The expected return value is undefined. A typical use for this method, is to copy configuration from config or instantiate plugin dependencies. This method is called when the plugin is registered to the client via `client.add()`.
+
+### Plugin.execute[](#pluginexecute "Permalink")
+
+This method contains the logic for processing events and has event as parameter. If used as enrichment type plugin, the expected return value is the modified/enriched event; while if used as a destination type plugin, the expected return value is a map with keys: `event` (BaseEvent), `code` (number), and `message` (string). This method is called for each event, including Identify, GroupIdentify and Revenue events, that's instrumented using the client interface.
+
+### Enrichment type plugin example[](#enrichment-type-plugin-example "Permalink")
+
+Here's an example of a plugin that modifies each event that's instrumented by adding an increment integer to `event_id` property of an event starting from 100.
+
+```ts
+import { init, add } from '@amplitude/analytics-react-native';
 import { ReactNativeConfig, EnrichmentPlugin, Event, PluginType } from '@amplitude/analytics-types';
 
 export class AddEventIdPlugin implements EnrichmentPlugin {
@@ -1858,7 +676,7 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
    * setup() is called on plugin installation
    * example: client.add(new AddEventIdPlugin());
    */
-  async setup(config: ReactNativeConfig): Promise<undefined> {
+  async setup(config: ReactNativeConfig): Promise {
      this.config = config;
      return;
   }
@@ -1867,7 +685,7 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
    * execute() is called on each event instrumented
    * example: client.track('New Event');
    */
-  async execute(event: Event): Promise<Event> {
+  async execute(event: Event): Promise {
     event.event_id = this.currentId++;
     return event;
   }
@@ -1875,10 +693,14 @@ export class AddEventIdPlugin implements EnrichmentPlugin {
 
 init('API_KEY');
 add(new AddEventIdPlugin());
-
-

Destination type plugin example

-

Here's an example of a plugin that sends each instrumented event to a target server URL using your preferred HTTP client.

-
import { init, add } from '@amplitude/analytics-react-native';
+```
+
+### Destination type plugin example[](#destination-type-plugin-example "Permalink")
+
+Here's an example of a plugin that sends each instrumented event to a target server URL using your preferred HTTP client.
+
+```ts
+import { init, add } from '@amplitude/analytics-react-native';
 import { ReactNativeConfig, DestinationPlugin, Event, PluginType, Result } from '@amplitude/analytics-types';
 
 export class MyDestinationPlugin implements DestinationPlugin {
@@ -1895,7 +717,7 @@ export class MyDestinationPlugin implements DestinationPlugin {
    * setup() is called on plugin installation
    * example: client.add(new MyDestinationPlugin());
    */
-  async setup(config: ReactNativeConfig): Promise<undefined> {
+  async setup(config: ReactNativeConfig): Promise {
     this.config = config;
     return;
   }
@@ -1904,7 +726,7 @@ export class MyDestinationPlugin implements DestinationPlugin {
    * execute() is called on each event instrumented
    * example: client.track('New Event');
    */
-  async execute(event: Event): Promise<Result> {
+  async execute(event: Event): Promise {
     const payload = { key: 'secret', data: event };
     const response = await fetch(this.serverUrl, {
       method: 'POST',
@@ -1924,14 +746,19 @@ export class MyDestinationPlugin implements DestinationPlugin {
 
 init('API_KEY');
 add(new MyDestinationPlugin('https://custom.domain.com'));
-
-

Advanced topics

-

Custom HTTP client

-

You can provide an implementation of Transport interface to the transportProvider configuration option for customization purpose, for example, sending requests to your proxy server with customized HTTP request headers.

-
import { Transport } from '@amplitude/analytics-types';
+```
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Custom HTTP client[](#custom-http-client "Permalink")
+
+You can provide an implementation of `Transport` interface to the `transportProvider` configuration option for customization purpose, for example, sending requests to your proxy server with customized HTTP request headers.
+
+```ts
+import { Transport } from '@amplitude/analytics-types';
 
 class MyTransport implements Transport {
-  async send(serverUrl: string, payload: Payload): Promise<Response | null> {
+  async send(serverUrl: string, payload: Payload): Promise {
     // check example: https://github.com/amplitude/Amplitude-TypeScript/blob/main/packages/analytics-client-common/src/transports/fetch.ts
   }
 }
@@ -1939,882 +766,157 @@ class MyTransport implements Transport {
 amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   transportProvider: new MyTransport(),
 });
-
-

Location

-

The Amplitude ingestion servers resolve event location in the following order:

-
    -
  1. User-provided city, country, region
  2. -
  3. Resolved from location_lat and location_lng
  4. -
  5. Resolved from ip
  6. -
-

By default, location will be determined by the ip on the server side. If you want more provide more granular location you can set city, country and region individually, or set location_lat and location_lng which will then be resolved to city, country and region on the server.
-Amplitude doesn't set precise location in the SDK to avoid extra permissions that my not be needed by all customers.

-

To set fine grain location, you can use an enrichment Plugin. Here is an example of how to set location_lat and location_lng.

-

Disabling IP tracking with ipAddress: false in TrackingOptions prevents location from being resolved on the backend. In this case you may want to create a Plugin like above to set any relevant location information yourself.

-

Carrier

-

Carrier support works on Android, but Apple stopped supporting it in iOS 16. In earlier versions of iOS, we fetch carrier info using CTCarrier and serviceSubscriberCellularProviders which are deprecated with no replacement.

-

Advertising Identifiers

-

Different platforms have different advertising identifiers. Due to user privacy concerns, Amplitude does not automatically collect these identifiers. However, it is easy to enable them using the instructions below. It is important to note that some identifiers are no longer recommended for use by the platform providers. Read the notes below before deciding to enable them.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PlatformAdvertising IdentifierRecommendedNotes
AndroidAppSetIdYesAppSetId is a unique identifier for the app instance. It is reset when the app is reinstalled.
AndroidADIDNoADID is a unique identifier for the device. It is reset when the user opts out of personalized ads.
iOSIDFVYesIDFV is a unique identifier for the app instance. It is reset when the app is reinstalled.
iOSIDFANoIDFA is a unique identifier for the device. It is reset when the user opts out of personalized ads.
-

Android

-
App set ID
-

App set ID is a unique identifier for each app install on a device. App set ID is reset by the user manually when they uninstall the app, or after 13 months of not opening the app. Google designed this as a privacy-friendly alternative to Ad ID for users who want to opt out of stronger analytics.

-

To use App Set ID, follow these steps.

-
    -
  1. -

    Add play-services-appset as a dependency to the Android project of your app.

    -
    dependencies {
    -    implementation 'com.google.android.gms:play-services-appset:16.0.2'
    -}
    -
    -
  2. -
  3. -

    Enable trackingOptions.appSetId

    -
    amplitude.init(API_KEY, OPTIONAL_USER_ID, {
    -  trackingOptions: {
    -    appSetId: true,
    -  },
    -});
    -
    -
  4. -
-
Android Ad ID
-

Android Ad ID is a unique identifier for each device. Android Ad ID is reset by the user manually when they opt out of personalized ads.

-

To use Android Ad ID, follow these steps.

-
    -
  1. -

    Add play-services-ads-identifier as a dependency to the Android project of your app. More detailed setup is described in our latest Android SDK docs.

    -
    dependencies {
    -  implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
    -}
    -
    -
  2. -
-

Android Ad Id is enabled by default. To disable it, set trackingOptions.adId to false.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  trackingOptions: {
-    adId: false,
-  },
-});
-
-

iOS

-
IDFV
-

IDFV is a unique identifier for the app instance. It is reset when the app is reinstalled.

-

To enable IDFV on iOS devices set trackingOptions.idfv to true.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  trackingOptions: {
-    idfv: true,
-  },
-});
-
-
IDFA
-

-

Warning

-IDFA is no longer recommended. You should consider using IDFV instead when possible.
-

-

IDFA is a unique identifier for the device. It is reset when the user opts out of personalized ads.

-

The React Native SDK does not directly access the IDFA as it would require adding the AdSupport.framework to your app. Instead you can use an Enrichment Plugin to set the IDFA yourself.

-

Here is an example Plugin that sets the IDFA using a third-party library.

-

Over the air updates (OTA)

-

If you are using platform like Expo that supports OTA updates. It is important to know our SDK has both native and JS code. If you are using OTA updates, you will need to make sure the native code is updated as well. See Expo's documentation on publishing and runtime versions for more details.

-

Below are versions of the SDK with the native code changes:

- - - - - - - - - - - -
@amplitude/analytics-react-native
1.3.0
- - -
-
+``` - - +### Location[](#location "Permalink") - +##### IDFA[](#idfa "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

July 23rd, 2024

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +IDFA is no longer recommended. You should consider using IDFV instead when possible. - - - - - - - - - - +@amplitude/analytics-react-native - - \ No newline at end of file +[1.3.0](https://github.com/amplitude/Amplitude-TypeScript/releases/tag/%40amplitude%2Fanalytics-react-native%401.3.0) \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-6/SKILL.md b/skills/integration/integration-react-react-router-6/SKILL.md index bf302892a..afcbbb861 100644 --- a/skills/integration/integration-react-react-router-6/SKILL.md +++ b/skills/integration/integration-react-react-router-6/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to React Router v6 applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React Router v6 example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-react-router-6/references/amplitude-quickstart.md b/skills/integration/integration-react-react-router-6/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-react-router-6/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-react-router-6/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-6/references/browser-sdk-2.md b/skills/integration/integration-react-react-router-6/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-react-router-6/references/browser-sdk-2.md +++ b/skills/integration/integration-react-react-router-6/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-6/references/browser-unified-sdk.md b/skills/integration/integration-react-react-router-6/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-react-router-6/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-react-router-6/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-data/SKILL.md b/skills/integration/integration-react-react-router-7-data/SKILL.md index 46aa8dacc..9e0efa499 100644 --- a/skills/integration/integration-react-react-router-7-data/SKILL.md +++ b/skills/integration/integration-react-react-router-7-data/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to React Router v7 - Data mode appl Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React Router v7 - Data mode example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-react-router-7-data/references/amplitude-quickstart.md b/skills/integration/integration-react-react-router-7-data/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-react-router-7-data/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-react-router-7-data/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-data/references/browser-sdk-2.md b/skills/integration/integration-react-react-router-7-data/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-react-router-7-data/references/browser-sdk-2.md +++ b/skills/integration/integration-react-react-router-7-data/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-data/references/browser-unified-sdk.md b/skills/integration/integration-react-react-router-7-data/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-react-router-7-data/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-react-router-7-data/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-declarative/SKILL.md b/skills/integration/integration-react-react-router-7-declarative/SKILL.md index 22bf0e841..d23a6556a 100644 --- a/skills/integration/integration-react-react-router-7-declarative/SKILL.md +++ b/skills/integration/integration-react-react-router-7-declarative/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to React Router v7 - Declarative mo Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React Router v7 - Declarative mode example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-react-router-7-declarative/references/amplitude-quickstart.md b/skills/integration/integration-react-react-router-7-declarative/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-react-router-7-declarative/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-react-router-7-declarative/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-declarative/references/browser-sdk-2.md b/skills/integration/integration-react-react-router-7-declarative/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-react-router-7-declarative/references/browser-sdk-2.md +++ b/skills/integration/integration-react-react-router-7-declarative/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-declarative/references/browser-unified-sdk.md b/skills/integration/integration-react-react-router-7-declarative/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-react-router-7-declarative/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-react-router-7-declarative/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-framework/SKILL.md b/skills/integration/integration-react-react-router-7-framework/SKILL.md index 9bdd02e6d..fcdd38ee9 100644 --- a/skills/integration/integration-react-react-router-7-framework/SKILL.md +++ b/skills/integration/integration-react-react-router-7-framework/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to React Router v7 - Framework mode Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React Router v7 - Framework mode example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-react-router-7-framework/references/amplitude-quickstart.md b/skills/integration/integration-react-react-router-7-framework/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-react-router-7-framework/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-react-router-7-framework/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-framework/references/browser-sdk-2.md b/skills/integration/integration-react-react-router-7-framework/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-react-router-7-framework/references/browser-sdk-2.md +++ b/skills/integration/integration-react-react-router-7-framework/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-react-router-7-framework/references/browser-unified-sdk.md b/skills/integration/integration-react-react-router-7-framework/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-react-router-7-framework/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-react-router-7-framework/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-code-based/SKILL.md b/skills/integration/integration-react-tanstack-router-code-based/SKILL.md index 26a715b1a..27fe1fca5 100644 --- a/skills/integration/integration-react-tanstack-router-code-based/SKILL.md +++ b/skills/integration/integration-react-tanstack-router-code-based/SKILL.md @@ -16,16 +16,16 @@ This skill helps you add Amplitude analytics to React with TanStack Router (code Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React with TanStack Router (code-based) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-tanstack-router-code-based/references/amplitude-quickstart.md b/skills/integration/integration-react-tanstack-router-code-based/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-tanstack-router-code-based/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-tanstack-router-code-based/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-code-based/references/browser-sdk-2.md b/skills/integration/integration-react-tanstack-router-code-based/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-tanstack-router-code-based/references/browser-sdk-2.md +++ b/skills/integration/integration-react-tanstack-router-code-based/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-code-based/references/browser-unified-sdk.md b/skills/integration/integration-react-tanstack-router-code-based/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-tanstack-router-code-based/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-tanstack-router-code-based/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-file-based/SKILL.md b/skills/integration/integration-react-tanstack-router-file-based/SKILL.md index 9d2fa1223..b5cafcd23 100644 --- a/skills/integration/integration-react-tanstack-router-file-based/SKILL.md +++ b/skills/integration/integration-react-tanstack-router-file-based/SKILL.md @@ -16,16 +16,16 @@ This skill helps you add Amplitude analytics to React with TanStack Router (file Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React with TanStack Router (file-based) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-tanstack-router-file-based/references/EXAMPLE.md b/skills/integration/integration-react-tanstack-router-file-based/references/EXAMPLE.md index f090c732f..5763d5385 100644 --- a/skills/integration/integration-react-tanstack-router-file-based/references/EXAMPLE.md +++ b/skills/integration/integration-react-tanstack-router-file-based/references/EXAMPLE.md @@ -641,109 +641,6 @@ function ProfilePage() { --- -## src/routeTree.gen.ts - -```ts -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -import { Route as rootRouteImport } from './routes/__root' -import { Route as ProfileRouteImport } from './routes/profile' -import { Route as BurritoRouteImport } from './routes/burrito' -import { Route as IndexRouteImport } from './routes/index' - -const ProfileRoute = ProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => rootRouteImport, -} as any) -const BurritoRoute = BurritoRouteImport.update({ - id: '/burrito', - path: '/burrito', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) - -export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute -} -export interface FileRoutesByTo { - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute -} -export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute -} -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/burrito' | '/profile' - fileRoutesByTo: FileRoutesByTo - to: '/' | '/burrito' | '/profile' - id: '__root__' | '/' | '/burrito' | '/profile' - fileRoutesById: FileRoutesById -} -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - BurritoRoute: typeof BurritoRoute - ProfileRoute: typeof ProfileRoute -} - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/profile': { - id: '/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof ProfileRouteImport - parentRoute: typeof rootRouteImport - } - '/burrito': { - id: '/burrito' - path: '/burrito' - fullPath: '/burrito' - preLoaderRoute: typeof BurritoRouteImport - parentRoute: typeof rootRouteImport - } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - } -} - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - BurritoRoute: BurritoRoute, - ProfileRoute: ProfileRoute, -} -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() - -``` - ---- - ## vite.config.ts ```ts diff --git a/skills/integration/integration-react-tanstack-router-file-based/references/amplitude-quickstart.md b/skills/integration/integration-react-tanstack-router-file-based/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-tanstack-router-file-based/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-tanstack-router-file-based/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-file-based/references/browser-sdk-2.md b/skills/integration/integration-react-tanstack-router-file-based/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-tanstack-router-file-based/references/browser-sdk-2.md +++ b/skills/integration/integration-react-tanstack-router-file-based/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-tanstack-router-file-based/references/browser-unified-sdk.md b/skills/integration/integration-react-tanstack-router-file-based/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-tanstack-router-file-based/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-tanstack-router-file-based/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-react-vite/SKILL.md b/skills/integration/integration-react-vite/SKILL.md index e54b35949..3f216a352 100644 --- a/skills/integration/integration-react-vite/SKILL.md +++ b/skills/integration/integration-react-vite/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to React (Vite) applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - React (Vite) example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-react-vite/references/amplitude-quickstart.md b/skills/integration/integration-react-vite/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-react-vite/references/amplitude-quickstart.md +++ b/skills/integration/integration-react-vite/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-react-vite/references/browser-sdk-2.md b/skills/integration/integration-react-vite/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-react-vite/references/browser-sdk-2.md +++ b/skills/integration/integration-react-vite/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-react-vite/references/browser-unified-sdk.md b/skills/integration/integration-react-vite/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-react-vite/references/browser-unified-sdk.md +++ b/skills/integration/integration-react-vite/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-ruby-on-rails/SKILL.md b/skills/integration/integration-ruby-on-rails/SKILL.md index 2f6917e2f..c6bee4635 100644 --- a/skills/integration/integration-ruby-on-rails/SKILL.md +++ b/skills/integration/integration-ruby-on-rails/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Ruby on Rails applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Ruby on Rails example project code -- `references/analytics.md` - Amplitude documentation for Analytics +- `references/analytics.md` - Amplitude analytics SDK catalog - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-ruby-on-rails/references/amplitude-quickstart.md b/skills/integration/integration-ruby-on-rails/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-ruby-on-rails/references/amplitude-quickstart.md +++ b/skills/integration/integration-ruby-on-rails/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-ruby-on-rails/references/analytics.md b/skills/integration/integration-ruby-on-rails/references/analytics.md index e73d554db..eafef6408 100644 --- a/skills/integration/integration-ruby-on-rails/references/analytics.md +++ b/skills/integration/integration-ruby-on-rails/references/analytics.md @@ -1,1778 +1,115 @@ - - - - - - - - - - - - - - - - - - Amplitude Analytics SDK Catalog | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
- - - - - - - -
-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - - - - \ No newline at end of file +[documentation](/docs) + +[Get Started](/docs/get-started) + +[Data](/docs/data) + +[Analytics](/docs/analytics) + +[Amplitude AI](/docs/amplitude-ai) + +[Session Replay](/docs/session-replay) + +[Guides and Surveys](/docs/guides-and-surveys) + +[AI Assistant](/docs/assistant) + +[Experiment](/docs/experiment-home) + +[Admin](/docs/admin) + +[Partners](/docs/partners) + +[FAQ](/docs/faq) + +[SDKs](/docs/sdks) + +/ + +[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) + +# Amplitude Analytics SDK Catalog + +[ + +Android + + + +](/docs/sdks/analytics/android)[ + +Browser + + + +](/docs/sdks/analytics/browser)[ + +Flutter + + + +](/docs/sdks/analytics/flutter)[ + +Go + + + +](/docs/sdks/analytics/go)[ + +iOS + + + +](/docs/sdks/analytics/ios)[ + +JRE Java + + + +](/docs/sdks/analytics/java)[ + +Node.js + + + +](/docs/sdks/analytics/node)[ + +Python + + + +](/docs/sdks/analytics/python)[ + +React Native + + + +](/docs/sdks/analytics/react-native)[ + +Unity + + + +](/docs/sdks/analytics/unity)[ + +Unreal Engine + + + +](/docs/sdks/analytics/unreal) + + + +Need help? [Contact Support](https://help.amplitude.com/hc/en-us/requests/new) + +Visit [Amplitude.com](https://www.amplitude.com) + +Have a look at the Amplitude [Blog](https://amplitude.com/blog) + +Learn more at [Amplitude Academy](https://academy.amplitude.com/) + +[](https://www.linkedin.com/company/amplitude-analytics)[](https://twitter.com/Amplitude_HQ)[](https://www.g2.com/products/amplitude-analytics/reviews) + +[Terms of Service](https://amplitude.com/terms) [Privacy Notice](https://amplitude.com/privacy) [Acceptable Use Policy](https://amplitude.com/aup) [Legal](https://amplitude.com/legal) + +© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc. \ No newline at end of file diff --git a/skills/integration/integration-ruby/SKILL.md b/skills/integration/integration-ruby/SKILL.md index 7c4070e30..26f649ad5 100644 --- a/skills/integration/integration-ruby/SKILL.md +++ b/skills/integration/integration-ruby/SKILL.md @@ -14,15 +14,15 @@ This skill helps you add Amplitude analytics to Ruby applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Ruby example project code -- `references/analytics.md` - Amplitude documentation for Analytics +- `references/analytics.md` - Amplitude analytics SDK catalog - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-ruby/references/amplitude-quickstart.md b/skills/integration/integration-ruby/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-ruby/references/amplitude-quickstart.md +++ b/skills/integration/integration-ruby/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-ruby/references/analytics.md b/skills/integration/integration-ruby/references/analytics.md index e73d554db..eafef6408 100644 --- a/skills/integration/integration-ruby/references/analytics.md +++ b/skills/integration/integration-ruby/references/analytics.md @@ -1,1778 +1,115 @@ - - - - - - - - - - - - - - - - - - Amplitude Analytics SDK Catalog | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
- - - - - - - -
-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - - - - \ No newline at end of file +[documentation](/docs) + +[Get Started](/docs/get-started) + +[Data](/docs/data) + +[Analytics](/docs/analytics) + +[Amplitude AI](/docs/amplitude-ai) + +[Session Replay](/docs/session-replay) + +[Guides and Surveys](/docs/guides-and-surveys) + +[AI Assistant](/docs/assistant) + +[Experiment](/docs/experiment-home) + +[Admin](/docs/admin) + +[Partners](/docs/partners) + +[FAQ](/docs/faq) + +[SDKs](/docs/sdks) + +/ + +[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) + +# Amplitude Analytics SDK Catalog + +[ + +Android + + + +](/docs/sdks/analytics/android)[ + +Browser + + + +](/docs/sdks/analytics/browser)[ + +Flutter + + + +](/docs/sdks/analytics/flutter)[ + +Go + + + +](/docs/sdks/analytics/go)[ + +iOS + + + +](/docs/sdks/analytics/ios)[ + +JRE Java + + + +](/docs/sdks/analytics/java)[ + +Node.js + + + +](/docs/sdks/analytics/node)[ + +Python + + + +](/docs/sdks/analytics/python)[ + +React Native + + + +](/docs/sdks/analytics/react-native)[ + +Unity + + + +](/docs/sdks/analytics/unity)[ + +Unreal Engine + + + +](/docs/sdks/analytics/unreal) + + + +Need help? [Contact Support](https://help.amplitude.com/hc/en-us/requests/new) + +Visit [Amplitude.com](https://www.amplitude.com) + +Have a look at the Amplitude [Blog](https://amplitude.com/blog) + +Learn more at [Amplitude Academy](https://academy.amplitude.com/) + +[](https://www.linkedin.com/company/amplitude-analytics)[](https://twitter.com/Amplitude_HQ)[](https://www.g2.com/products/amplitude-analytics/reviews) + +[Terms of Service](https://amplitude.com/terms) [Privacy Notice](https://amplitude.com/privacy) [Acceptable Use Policy](https://amplitude.com/aup) [Legal](https://amplitude.com/legal) + +© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc. \ No newline at end of file diff --git a/skills/integration/integration-sveltekit/SKILL.md b/skills/integration/integration-sveltekit/SKILL.md index b71260e0d..f03238ac6 100644 --- a/skills/integration/integration-sveltekit/SKILL.md +++ b/skills/integration/integration-sveltekit/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to SvelteKit applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - SvelteKit example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-sveltekit/references/EXAMPLE.md b/skills/integration/integration-sveltekit/references/EXAMPLE.md index 5f7926a1a..aa3b3d70b 100644 --- a/skills/integration/integration-sveltekit/references/EXAMPLE.md +++ b/skills/integration/integration-sveltekit/references/EXAMPLE.md @@ -178,13428 +178,6 @@ engine-strict=true --- -## .svelte-kit/ambient.d.ts - -```ts - -// this file is generated — do not edit it - - -/// - -/** - * This module provides access to environment variables that are injected _statically_ into your bundle at build time and are limited to _private_ access. - * - * | | Runtime | Build time | - * | ------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | - * | Private | [`$env/dynamic/private`](https://svelte.dev/docs/kit/$env-dynamic-private) | [`$env/static/private`](https://svelte.dev/docs/kit/$env-static-private) | - * | Public | [`$env/dynamic/public`](https://svelte.dev/docs/kit/$env-dynamic-public) | [`$env/static/public`](https://svelte.dev/docs/kit/$env-static-public) | - * - * Static environment variables are [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env` at build time and then statically injected into your bundle at build time, enabling optimisations like dead code elimination. - * - * **_Private_ access:** - * - * - This module cannot be imported into client-side code - * - This module only includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://svelte.dev/docs/kit/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://svelte.dev/docs/kit/configuration#env) (if configured) - * - * For example, given the following build time environment: - * - * ```env - * ENVIRONMENT=production - * PUBLIC_BASE_URL=http://site.com - * ``` - * - * With the default `publicPrefix` and `privatePrefix`: - * - * ```ts - * import { ENVIRONMENT, PUBLIC_BASE_URL } from '$env/static/private'; - * - * console.log(ENVIRONMENT); // => "production" - * console.log(PUBLIC_BASE_URL); // => throws error during build - * ``` - * - * The above values will be the same _even if_ different values for `ENVIRONMENT` or `PUBLIC_BASE_URL` are set at runtime, as they are statically replaced in your code with their build time values. - */ -declare module '$env/static/private' { - export const NVM_INC: string; - export const _ZO_DOCTOR: string; - export const DD_INSTRUMENTATION_TELEMETRY_ENABLED: string; - export const MANPATH: string; - export const COREPACK_ROOT: string; - export const TERM_PROGRAM: string; - export const NODE: string; - export const PYENV_ROOT: string; - export const NVM_CD_FLAGS: string; - export const INIT_CWD: string; - export const TERM: string; - export const SHELL: string; - export const DD_TRACE_ENABLED: string; - export const TMPDIR: string; - export const GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE: string; - export const VSCODE_PYTHON_AUTOACTIVATE_GUARD: string; - export const TERM_PROGRAM_VERSION: string; - export const SUDO_ASKPASS: string; - export const npm_config_npm_globalconfig: string; - export const DFX_WARNING: string; - export const ZDOTDIR: string; - export const MallocNanoZone: string; - export const TERM_SESSION_ID: string; - export const NO_COLOR: string; - export const CURSOR_ASKPASS_SECRET: string; - export const npm_config_registry: string; - export const PNPM_HOME: string; - export const ZSH: string; - export const NVM_DIR: string; - export const USER: string; - export const LS_COLORS: string; - export const GUILE_LOAD_PATH: string; - export const COMMAND_MODE: string; - export const AMP_REPO_ROOT: string; - export const npm_config_globalconfig: string; - export const PNPM_SCRIPT_SRC_DIR: string; - export const CLAUDE_CODE_SSE_PORT: string; - export const SSH_AUTH_SOCK: string; - export const VSCODE_PROFILE_INITIALIZED: string; - export const GUILE_LOAD_COMPILED_PATH: string; - export const __CF_USER_TEXT_ENCODING: string; - export const TERM_FEATURES: string; - export const npm_execpath: string; - export const PAGER: string; - export const GOOGLE_CLOUD_PROJECT: string; - export const LSCOLORS: string; - export const npm_config_frozen_lockfile: string; - export const npm_config_verify_deps_before_run: string; - export const TERMINFO_DIRS: string; - export const PATH: string; - export const LaunchInstanceID: string; - export const npm_config_engine_strict: string; - export const npm_package_json: string; - export const USER_ZDOTDIR: string; - export const __CFBundleIdentifier: string; - export const COREPACK_ENABLE_DOWNLOAD_PROMPT: string; - export const PWD: string; - export const npm_command: string; - export const AMPLITUDE_USER: string; - export const npm_config__jsr_registry: string; - export const npm_lifecycle_event: string; - export const LANG: string; - export const CURSOR_AGENT: string; - export const npm_package_name: string; - export const PYTHONSTARTUP: string; - export const ITERM_PROFILE: string; - export const NODE_PATH: string; - export const VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; - export const XPC_FLAGS: string; - export const MACH_PORT_RENDEZVOUS_PEER_VALDATION: string; - export const FORCE_COLOR: string; - export const CURSOR_INVOKED_AS: string; - export const npm_config_node_gyp: string; - export const XPC_SERVICE_NAME: string; - export const npm_package_version: string; - export const pnpm_config_verify_deps_before_run: string; - export const VSCODE_INJECTION: string; - export const COLORFGBG: string; - export const SHLVL: string; - export const PYENV_SHELL: string; - export const HOME: string; - export const VSCODE_GIT_ASKPASS_MAIN: string; - export const PYTHON_BASIC_REPL: string; - export const LC_TERMINAL_VERSION: string; - export const NODE_COMPILE_CACHE: string; - export const CURSOR_ASKPASS_SOCKET: string; - export const ITERM_SESSION_ID: string; - export const LESS: string; - export const LOGNAME: string; - export const npm_lifecycle_script: string; - export const VSCODE_GIT_IPC_HANDLE: string; - export const GUILE_SYSTEM_EXTENSIONS_PATH: string; - export const NVM_BIN: string; - export const BUN_INSTALL: string; - export const npm_config_user_agent: string; - export const VSCODE_GIT_ASKPASS_NODE: string; - export const GIT_ASKPASS: string; - export const LC_TERMINAL: string; - export const SECURITYSESSIONID: string; - export const COLORTERM: string; - export const npm_node_execpath: string; - export const NODE_ENV: string; -} - -/** - * This module provides access to environment variables that are injected _statically_ into your bundle at build time and are _publicly_ accessible. - * - * | | Runtime | Build time | - * | ------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | - * | Private | [`$env/dynamic/private`](https://svelte.dev/docs/kit/$env-dynamic-private) | [`$env/static/private`](https://svelte.dev/docs/kit/$env-static-private) | - * | Public | [`$env/dynamic/public`](https://svelte.dev/docs/kit/$env-dynamic-public) | [`$env/static/public`](https://svelte.dev/docs/kit/$env-static-public) | - * - * Static environment variables are [loaded by Vite](https://vitejs.dev/guide/env-and-mode.html#env-files) from `.env` files and `process.env` at build time and then statically injected into your bundle at build time, enabling optimisations like dead code elimination. - * - * **_Public_ access:** - * - * - This module _can_ be imported into client-side code - * - **Only** variables that begin with [`config.kit.env.publicPrefix`](https://svelte.dev/docs/kit/configuration#env) (which defaults to `PUBLIC_`) are included - * - * For example, given the following build time environment: - * - * ```env - * ENVIRONMENT=production - * PUBLIC_BASE_URL=http://site.com - * ``` - * - * With the default `publicPrefix` and `privatePrefix`: - * - * ```ts - * import { ENVIRONMENT, PUBLIC_BASE_URL } from '$env/static/public'; - * - * console.log(ENVIRONMENT); // => throws error during build - * console.log(PUBLIC_BASE_URL); // => "http://site.com" - * ``` - * - * The above values will be the same _even if_ different values for `ENVIRONMENT` or `PUBLIC_BASE_URL` are set at runtime, as they are statically replaced in your code with their build time values. - */ -declare module '$env/static/public' { - -} - -/** - * This module provides access to environment variables set _dynamically_ at runtime and that are limited to _private_ access. - * - * | | Runtime | Build time | - * | ------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | - * | Private | [`$env/dynamic/private`](https://svelte.dev/docs/kit/$env-dynamic-private) | [`$env/static/private`](https://svelte.dev/docs/kit/$env-static-private) | - * | Public | [`$env/dynamic/public`](https://svelte.dev/docs/kit/$env-dynamic-public) | [`$env/static/public`](https://svelte.dev/docs/kit/$env-static-public) | - * - * Dynamic environment variables are defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node) (or running [`vite preview`](https://svelte.dev/docs/kit/cli)), this is equivalent to `process.env`. - * - * **_Private_ access:** - * - * - This module cannot be imported into client-side code - * - This module includes variables that _do not_ begin with [`config.kit.env.publicPrefix`](https://svelte.dev/docs/kit/configuration#env) _and do_ start with [`config.kit.env.privatePrefix`](https://svelte.dev/docs/kit/configuration#env) (if configured) - * - * > [!NOTE] In `dev`, `$env/dynamic` includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter. - * - * > [!NOTE] To get correct types, environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed: - * > - * > ```env - * > MY_FEATURE_FLAG= - * > ``` - * > - * > You can override `.env` values from the command line like so: - * > - * > ```sh - * > MY_FEATURE_FLAG="enabled" npm run dev - * > ``` - * - * For example, given the following runtime environment: - * - * ```env - * ENVIRONMENT=production - * PUBLIC_BASE_URL=http://site.com - * ``` - * - * With the default `publicPrefix` and `privatePrefix`: - * - * ```ts - * import { env } from '$env/dynamic/private'; - * - * console.log(env.ENVIRONMENT); // => "production" - * console.log(env.PUBLIC_BASE_URL); // => undefined - * ``` - */ -declare module '$env/dynamic/private' { - export const env: { - NVM_INC: string; - _ZO_DOCTOR: string; - DD_INSTRUMENTATION_TELEMETRY_ENABLED: string; - MANPATH: string; - COREPACK_ROOT: string; - TERM_PROGRAM: string; - NODE: string; - PYENV_ROOT: string; - NVM_CD_FLAGS: string; - INIT_CWD: string; - TERM: string; - SHELL: string; - DD_TRACE_ENABLED: string; - TMPDIR: string; - GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE: string; - VSCODE_PYTHON_AUTOACTIVATE_GUARD: string; - TERM_PROGRAM_VERSION: string; - SUDO_ASKPASS: string; - npm_config_npm_globalconfig: string; - DFX_WARNING: string; - ZDOTDIR: string; - MallocNanoZone: string; - TERM_SESSION_ID: string; - NO_COLOR: string; - CURSOR_ASKPASS_SECRET: string; - npm_config_registry: string; - PNPM_HOME: string; - ZSH: string; - NVM_DIR: string; - USER: string; - LS_COLORS: string; - GUILE_LOAD_PATH: string; - COMMAND_MODE: string; - AMP_REPO_ROOT: string; - npm_config_globalconfig: string; - PNPM_SCRIPT_SRC_DIR: string; - CLAUDE_CODE_SSE_PORT: string; - SSH_AUTH_SOCK: string; - VSCODE_PROFILE_INITIALIZED: string; - GUILE_LOAD_COMPILED_PATH: string; - __CF_USER_TEXT_ENCODING: string; - TERM_FEATURES: string; - npm_execpath: string; - PAGER: string; - GOOGLE_CLOUD_PROJECT: string; - LSCOLORS: string; - npm_config_frozen_lockfile: string; - npm_config_verify_deps_before_run: string; - TERMINFO_DIRS: string; - PATH: string; - LaunchInstanceID: string; - npm_config_engine_strict: string; - npm_package_json: string; - USER_ZDOTDIR: string; - __CFBundleIdentifier: string; - COREPACK_ENABLE_DOWNLOAD_PROMPT: string; - PWD: string; - npm_command: string; - AMPLITUDE_USER: string; - npm_config__jsr_registry: string; - npm_lifecycle_event: string; - LANG: string; - CURSOR_AGENT: string; - npm_package_name: string; - PYTHONSTARTUP: string; - ITERM_PROFILE: string; - NODE_PATH: string; - VSCODE_GIT_ASKPASS_EXTRA_ARGS: string; - XPC_FLAGS: string; - MACH_PORT_RENDEZVOUS_PEER_VALDATION: string; - FORCE_COLOR: string; - CURSOR_INVOKED_AS: string; - npm_config_node_gyp: string; - XPC_SERVICE_NAME: string; - npm_package_version: string; - pnpm_config_verify_deps_before_run: string; - VSCODE_INJECTION: string; - COLORFGBG: string; - SHLVL: string; - PYENV_SHELL: string; - HOME: string; - VSCODE_GIT_ASKPASS_MAIN: string; - PYTHON_BASIC_REPL: string; - LC_TERMINAL_VERSION: string; - NODE_COMPILE_CACHE: string; - CURSOR_ASKPASS_SOCKET: string; - ITERM_SESSION_ID: string; - LESS: string; - LOGNAME: string; - npm_lifecycle_script: string; - VSCODE_GIT_IPC_HANDLE: string; - GUILE_SYSTEM_EXTENSIONS_PATH: string; - NVM_BIN: string; - BUN_INSTALL: string; - npm_config_user_agent: string; - VSCODE_GIT_ASKPASS_NODE: string; - GIT_ASKPASS: string; - LC_TERMINAL: string; - SECURITYSESSIONID: string; - COLORTERM: string; - npm_node_execpath: string; - NODE_ENV: string; - [key: `PUBLIC_${string}`]: undefined; - [key: `${string}`]: string | undefined; - } -} - -/** - * This module provides access to environment variables set _dynamically_ at runtime and that are _publicly_ accessible. - * - * | | Runtime | Build time | - * | ------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------ | - * | Private | [`$env/dynamic/private`](https://svelte.dev/docs/kit/$env-dynamic-private) | [`$env/static/private`](https://svelte.dev/docs/kit/$env-static-private) | - * | Public | [`$env/dynamic/public`](https://svelte.dev/docs/kit/$env-dynamic-public) | [`$env/static/public`](https://svelte.dev/docs/kit/$env-static-public) | - * - * Dynamic environment variables are defined by the platform you're running on. For example if you're using [`adapter-node`](https://github.com/sveltejs/kit/tree/main/packages/adapter-node) (or running [`vite preview`](https://svelte.dev/docs/kit/cli)), this is equivalent to `process.env`. - * - * **_Public_ access:** - * - * - This module _can_ be imported into client-side code - * - **Only** variables that begin with [`config.kit.env.publicPrefix`](https://svelte.dev/docs/kit/configuration#env) (which defaults to `PUBLIC_`) are included - * - * > [!NOTE] In `dev`, `$env/dynamic` includes environment variables from `.env`. In `prod`, this behavior will depend on your adapter. - * - * > [!NOTE] To get correct types, environment variables referenced in your code should be declared (for example in an `.env` file), even if they don't have a value until the app is deployed: - * > - * > ```env - * > MY_FEATURE_FLAG= - * > ``` - * > - * > You can override `.env` values from the command line like so: - * > - * > ```sh - * > MY_FEATURE_FLAG="enabled" npm run dev - * > ``` - * - * For example, given the following runtime environment: - * - * ```env - * ENVIRONMENT=production - * PUBLIC_BASE_URL=http://example.com - * ``` - * - * With the default `publicPrefix` and `privatePrefix`: - * - * ```ts - * import { env } from '$env/dynamic/public'; - * console.log(env.ENVIRONMENT); // => undefined, not public - * console.log(env.PUBLIC_BASE_URL); // => "http://example.com" - * ``` - * - * ``` - * - * ``` - */ -declare module '$env/dynamic/public' { - export const env: { - [key: `PUBLIC_${string}`]: string | undefined; - } -} - -``` - ---- - -## .svelte-kit/generated/client-optimized/app.js - -```js -import * as client_hooks from '../../../src/hooks.client.ts'; - - -export { matchers } from './matchers.js'; - -export const nodes = [ - () => import('./nodes/0'), - () => import('./nodes/1'), - () => import('./nodes/2'), - () => import('./nodes/3'), - () => import('./nodes/4') -]; - -export const server_loads = []; - -export const dictionary = { - "/": [2], - "/burrito": [3], - "/profile": [4] - }; - -export const hooks = { - handleError: client_hooks.handleError || (({ error }) => { console.error(error) }), - init: client_hooks.init, - reroute: (() => {}), - transport: {} -}; - -export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode])); -export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode])); - -export const hash = false; - -export const decode = (type, value) => decoders[type](value); - -export { default as root } from '../root.js'; -``` - ---- - -## .svelte-kit/generated/client-optimized/matchers.js - -```js -export const matchers = {}; -``` - ---- - -## .svelte-kit/generated/client-optimized/nodes/0.js - -```js -export { default as component } from "../../../../src/routes/+layout.svelte"; -``` - ---- - -## .svelte-kit/generated/client-optimized/nodes/1.js - -```js -export { default as component } from "../../../../node_modules/.pnpm/@sveltejs+kit@2.56.1_@sveltejs+vite-plugin-svelte@6.2.4_svelte@5.55.2_vite@7.3.2__svelt_151ca1313abe6eb03c68c3dd144be4dc/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte"; -``` - ---- - -## .svelte-kit/generated/client-optimized/nodes/2.js - -```js -export { default as component } from "../../../../src/routes/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/client-optimized/nodes/3.js - -```js -export { default as component } from "../../../../src/routes/burrito/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/client-optimized/nodes/4.js - -```js -export { default as component } from "../../../../src/routes/profile/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/client/app.js - -```js -import * as client_hooks from '../../../src/hooks.client.ts'; - - -export { matchers } from './matchers.js'; - -export const nodes = [ - () => import('./nodes/0'), - () => import('./nodes/1'), - () => import('./nodes/2'), - () => import('./nodes/3'), - () => import('./nodes/4') -]; - -export const server_loads = []; - -export const dictionary = { - "/": [2], - "/burrito": [3], - "/profile": [4] - }; - -export const hooks = { - handleError: client_hooks.handleError || (({ error }) => { console.error(error) }), - init: client_hooks.init, - reroute: (() => {}), - transport: {} -}; - -export const decoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.decode])); -export const encoders = Object.fromEntries(Object.entries(hooks.transport).map(([k, v]) => [k, v.encode])); - -export const hash = false; - -export const decode = (type, value) => decoders[type](value); - -export { default as root } from '../root.js'; -``` - ---- - -## .svelte-kit/generated/client/matchers.js - -```js -export const matchers = {}; -``` - ---- - -## .svelte-kit/generated/client/nodes/0.js - -```js -export { default as component } from "../../../../src/routes/+layout.svelte"; -``` - ---- - -## .svelte-kit/generated/client/nodes/1.js - -```js -export { default as component } from "../../../../node_modules/.pnpm/@sveltejs+kit@2.56.1_@sveltejs+vite-plugin-svelte@6.2.4_svelte@5.55.2_vite@7.3.2__svelt_151ca1313abe6eb03c68c3dd144be4dc/node_modules/@sveltejs/kit/src/runtime/components/svelte-5/error.svelte"; -``` - ---- - -## .svelte-kit/generated/client/nodes/2.js - -```js -export { default as component } from "../../../../src/routes/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/client/nodes/3.js - -```js -export { default as component } from "../../../../src/routes/burrito/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/client/nodes/4.js - -```js -export { default as component } from "../../../../src/routes/profile/+page.svelte"; -``` - ---- - -## .svelte-kit/generated/root.js - -```js -import { asClassComponent } from 'svelte/legacy'; -import Root from './root.svelte'; -export default asClassComponent(Root); -``` - ---- - -## .svelte-kit/generated/root.svelte - -```svelte - - - - -{#if constructors[1]} - {@const Pyramid_0 = constructors[0]} - - - - - - -{:else} - {@const Pyramid_0 = constructors[0]} - - - -{/if} - -{#if mounted} -
- {#if navigated} - {title} - {/if} -
-{/if} -``` - ---- - -## .svelte-kit/generated/server/internal.js - -```js - -import root from '../root.js'; -import { set_building, set_prerendering } from '__sveltekit/environment'; -import { set_assets } from '$app/paths/internal/server'; -import { set_manifest, set_read_implementation } from '__sveltekit/server'; -import { set_private_env, set_public_env } from '../../../node_modules/.pnpm/@sveltejs+kit@2.56.1_@sveltejs+vite-plugin-svelte@6.2.4_svelte@5.55.2_vite@7.3.2__svelt_151ca1313abe6eb03c68c3dd144be4dc/node_modules/@sveltejs/kit/src/runtime/shared-server.js'; - -export const options = { - app_template_contains_nonce: false, - async: false, - csp: {"mode":"auto","directives":{"upgrade-insecure-requests":false,"block-all-mixed-content":false},"reportOnly":{"upgrade-insecure-requests":false,"block-all-mixed-content":false}}, - csrf_check_origin: true, - csrf_trusted_origins: [], - embedded: false, - env_public_prefix: 'PUBLIC_', - env_private_prefix: '', - hash_routing: false, - hooks: null, // added lazily, via `get_hooks` - preload_strategy: "modulepreload", - root, - service_worker: false, - service_worker_options: undefined, - server_error_boundaries: false, - templates: { - app: ({ head, body, assets, nonce, env }) => "\n\n\t\n\t\t\n\t\t\n\t\t" + head + "\n\t\n\t\n\t\t
" + body + "
\n\t\n\n", - error: ({ status, message }) => "\n\n\t\n\t\t\n\t\t" + message + "\n\n\t\t\n\t\n\t\n\t\t
\n\t\t\t" + status + "\n\t\t\t
\n\t\t\t\t

" + message + "

\n\t\t\t
\n\t\t
\n\t\n\n" - }, - version_hash: "1bdh8ft" -}; - -export async function get_hooks() { - let handle; - let handleFetch; - let handleError; - let handleValidationError; - let init; - ({ handle, handleFetch, handleError, handleValidationError, init } = await import("../../../src/hooks.server.ts")); - - let reroute; - let transport; - - - return { - handle, - handleFetch, - handleError, - handleValidationError, - init, - reroute, - transport - }; -} - -export { set_assets, set_building, set_manifest, set_prerendering, set_private_env, set_public_env, set_read_implementation }; - -``` - ---- - -## .svelte-kit/non-ambient.d.ts - -```ts - -// this file is generated — do not edit it - - -declare module "svelte/elements" { - export interface HTMLAttributes { - 'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null; - 'data-sveltekit-noscroll'?: true | '' | 'off' | undefined | null; - 'data-sveltekit-preload-code'?: - | true - | '' - | 'eager' - | 'viewport' - | 'hover' - | 'tap' - | 'off' - | undefined - | null; - 'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null; - 'data-sveltekit-reload'?: true | '' | 'off' | undefined | null; - 'data-sveltekit-replacestate'?: true | '' | 'off' | undefined | null; - } -} - -export {}; - - -declare module "$app/types" { - type MatcherParam = M extends (param : string) => param is (infer U extends string) ? U : string; - - export interface AppTypes { - RouteId(): "/" | "/api" | "/api/auth" | "/api/auth/login" | "/burrito" | "/profile"; - RouteParams(): { - - }; - LayoutParams(): { - "/": Record; - "/api": Record; - "/api/auth": Record; - "/api/auth/login": Record; - "/burrito": Record; - "/profile": Record - }; - Pathname(): "/" | "/api/auth/login" | "/burrito" | "/profile"; - ResolvedPathname(): `${"" | `/${string}`}${ReturnType}`; - Asset(): "/robots.txt" | string & {}; - } -} -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/3TW-MQft.js - -```js -var Ht=Array.isArray,Bt=Array.prototype.indexOf,ie=Array.prototype.includes,Yn=Array.from,qn=Object.defineProperty,de=Object.getOwnPropertyDescriptor,Vt=Object.getOwnPropertyDescriptors,zt=Object.prototype,Gt=Array.prototype,at=Object.getPrototypeOf,Qe=Object.isExtensible;const Kt=()=>{};function Un(e){return e()}function $t(e){for(var t=0;t{e=r,t=s});return{promise:n,resolve:e,reject:t}}const b=2,le=4,ye=8,ut=1<<24,M=16,F=32,J=64,Wt=128,k=512,g=1024,T=2048,j=4096,I=8192,C=16384,re=32768,et=1<<25,Se=65536,Me=1<<17,Xt=1<<18,ge=1<<19,ct=1<<20,Q=65536,Fe=1<<21,pe=1<<22,H=1<<23,ve=Symbol("$state"),Hn=Symbol("legacy props"),Bn=Symbol(""),q=new class extends Error{name="StaleReactionError";message="The reaction that called `getAbortSignal()` was re-run or destroyed"},zn=!!globalThis.document?.contentType&&globalThis.document.contentType.includes("xml"),Ne=3,_t=8;function Zt(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}function Jt(){throw new Error("https://svelte.dev/e/async_derived_orphan")}function Qt(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function en(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function tn(e){throw new Error("https://svelte.dev/e/effect_orphan")}function nn(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function Gn(){throw new Error("https://svelte.dev/e/hydration_failed")}function Kn(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function rn(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function sn(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function ln(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function $n(){throw new Error("https://svelte.dev/e/svelte_boundary_reset_onerror")}const Wn=1,Xn=2,Zn=4,Jn=8,Qn=16,er=1,tr=2,fn="[",an="[!",nr="[?",on="]",He={},m=Symbol(),un="http://www.w3.org/1999/xhtml";function Be(e){console.warn("https://svelte.dev/e/hydration_mismatch")}function rr(){console.warn("https://svelte.dev/e/svelte_boundary_reset_noop")}let ee=!1;function sr(e){ee=e}let S;function fe(e){if(e===null)throw Be(),He;return S=e}function ir(){return fe(z(S))}function lr(e){if(ee){if(z(S)!==null)throw Be(),He;S=e}}function fr(e=1){if(ee){for(var t=e,n=S;t--;)n=z(n);S=n}}function ar(e=!0){for(var t=0,n=S;;){if(n.nodeType===_t){var r=n.data;if(r===on){if(t===0)return n;t-=1}else(r===fn||r===an||r[0]==="["&&!isNaN(Number(r.slice(1))))&&(t+=1)}var s=z(n);e&&n.remove(),n=s}}function or(e){if(!e||e.nodeType!==_t)throw Be(),He;return e.data}function dt(e){return e===this.v}function cn(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function vt(e){return!cn(e,this.v)}let De=!1;function ur(){De=!0}let E=null;function Ae(e){E=e}function cr(e){return ht().get(e)}function _r(e,t){return ht().set(e,t),t}function dr(e,t=!1,n){E={p:E,i:!1,c:null,e:null,s:e,x:null,r:v,l:De&&!t?{s:null,u:null,$:[]}:null}}function vr(e){var t=E,n=t.e;if(n!==null){t.e=null;for(var r of n)Nt(r)}return t.i=!0,E=t.p,{}}function me(){return!De||E!==null&&E.l===null}function ht(e){return E===null&&Zt(),E.c??=new Map(_n(E)||void 0)}function _n(e){let t=e.p;for(;t!==null;){const n=t.c;if(n!==null)return n;t=t.p}return null}let $=[];function pt(){var e=$;$=[],$t(e)}function tt(e){if($.length===0&&!he){var t=$;queueMicrotask(()=>{t===$&&pt()})}$.push(e)}function dn(){for(;$.length>0;)pt()}function vn(e){var t=v;if(t===null)return d.f|=H,e;if((t.f&re)===0&&(t.f&le)===0)throw e;Re(e,t)}function Re(e,t){for(;t!==null;){if((t.f&Wt)!==0){if((t.f&re)===0)throw e;try{t.b.error(e);return}catch(n){e=n}}t=t.parent}throw e}const hn=-7169;function y(e,t){e.f=e.f&hn|t}function Ve(e){(e.f&k)!==0||e.deps===null?y(e,g):y(e,j)}function wt(e){if(e!==null)for(const t of e)(t.f&b)===0||(t.f&Q)===0||(t.f^=Q,wt(t.deps))}function pn(e,t,n){(e.f&T)!==0?t.add(e):(e.f&j)!==0&&n.add(e),wt(e.deps),y(e,g)}const G=new Set;let w=null,D=null,je=null,he=!1,Ie=!1,se=null,Te=null;var nt=0;let wn=1;class B{id=wn++;current=new Map;previous=new Map;#f=new Set;#a=new Set;#r=new Map;#t=new Map;#v=null;#e=[];#h=[];#i=new Set;#s=new Set;#n=new Map;#o=new Set;is_fork=!1;#u=!1;#l=new Set;#c(){return this.is_fork||this.#t.size>0}#w(){for(const r of this.#l)for(const s of r.#t.keys()){for(var t=!1,n=s;n.parent!==null;){if(this.#n.has(n)){t=!0;break}n=n.parent}if(!t)return!0}return!1}skip_effect(t){this.#n.has(t)||this.#n.set(t,{d:[],m:[]}),this.#o.delete(t)}unskip_effect(t,n=r=>this.schedule(r)){var r=this.#n.get(t);if(r){this.#n.delete(t);for(var s of r.d)y(s,T),n(s);for(s of r.m)y(s,j),n(s)}this.#o.add(t)}#_(){if(nt++>1e3&&(G.delete(this),gn()),!this.#c()){for(const l of this.#i)this.#s.delete(l),y(l,T),this.schedule(l);for(const l of this.#s)y(l,j),this.schedule(l)}const t=this.#e;this.#e=[],this.apply();var n=se=[],r=[],s=Te=[];for(const l of t)try{this.#p(l,n,r)}catch(f){throw mt(l),f}if(w=null,s.length>0){var i=B.ensure();for(const l of s)i.schedule(l)}if(se=null,Te=null,this.#c()||this.#w()){this.#d(r),this.#d(n);for(const[l,f]of this.#n)gt(l,f)}else{this.#r.size===0&&G.delete(this),this.#i.clear(),this.#s.clear();for(const l of this.#f)l(this);this.#f.clear(),rt(r),rt(n),this.#v?.resolve()}var o=w;if(this.#e.length>0){const l=o??=this;l.#e.push(...this.#e.filter(f=>!l.#e.includes(f)))}o!==null&&(G.add(o),o.#_())}#p(t,n,r){t.f^=g;for(var s=t.first;s!==null;){var i=s.f,o=(i&(F|J))!==0,l=o&&(i&g)!==0,f=l||(i&I)!==0||this.#n.has(s);if(!f&&s.fn!==null){o?s.f^=g:(i&le)!==0?n.push(s):Ee(s)&&((i&M)!==0&&this.#s.add(s),oe(s));var u=s.first;if(u!==null){s=u;continue}}for(;s!==null;){var a=s.next;if(a!==null){s=a;break}s=s.parent}}}#d(t){for(var n=0;n!this.current.has(c));if(s.length===0)t&&a.discard();else if(n.length>0){if(t)for(const c of this.#o)a.unskip_effect(c,_=>{(_.f&(M|pe))!==0?a.schedule(_):a.#d([_])});a.activate();var i=new Set,o=new Map;for(var l of n)yt(l,s,i,o);o=new Map;var f=[...a.current.keys()].filter(c=>this.current.has(c)?this.current.get(c)[0]!==c:!0);for(const c of this.#h)(c.f&(C|I|Me))===0&&ze(c,f,o)&&((c.f&(pe|M))!==0?(y(c,T),a.schedule(c)):a.#i.add(c));if(a.#e.length>0){a.apply();for(var u of a.#e)a.#p(u,[],[]);a.#e=[]}a.deactivate()}}for(const a of G)a.#l.has(this)&&(a.#l.delete(this),a.#l.size===0&&!a.#c()&&(a.activate(),a.#_()))}increment(t,n){let r=this.#r.get(n)??0;if(this.#r.set(n,r+1),t){let s=this.#t.get(n)??0;this.#t.set(n,s+1)}}decrement(t,n,r){let s=this.#r.get(n)??0;if(s===1?this.#r.delete(n):this.#r.set(n,s-1),t){let i=this.#t.get(n)??0;i===1?this.#t.delete(n):this.#t.set(n,i-1)}this.#u||r||(this.#u=!0,tt(()=>{this.#u=!1,this.flush()}))}transfer_effects(t,n){for(const r of t)this.#i.add(r);for(const r of n)this.#s.add(r);t.clear(),n.clear()}oncommit(t){this.#f.add(t)}ondiscard(t){this.#a.add(t)}settled(){return(this.#v??=ot()).promise}static ensure(){if(w===null){const t=w=new B;Ie||(G.add(w),he||tt(()=>{w===t&&t.flush()}))}return w}apply(){{D=null;return}}schedule(t){if(je=t,t.b?.is_pending&&(t.f&(le|ye|ut))!==0&&(t.f&re)===0){t.b.defer_effect(t);return}for(var n=t;n.parent!==null;){n=n.parent;var r=n.f;if(se!==null&&n===v&&(d===null||(d.f&b)===0))return;if((r&(J|F))!==0){if((r&g)===0)return;n.f^=g}}this.#e.push(n)}}function yn(e){var t=he;he=!0;try{for(var n;;){if(dn(),w===null)return n;w.flush()}}finally{he=t}}function gn(){try{nn()}catch(e){Re(e,je)}}let Y=null;function rt(e){var t=e.length;if(t!==0){for(var n=0;n0)){X.clear();for(const s of Y){if((s.f&(C|I))!==0)continue;const i=[s];let o=s.parent;for(;o!==null;)Y.has(o)&&(Y.delete(o),i.push(o)),o=o.parent;for(let l=i.length-1;l>=0;l--){const f=i[l];(f.f&(C|I))===0&&oe(f)}}Y.clear()}}Y=null}}function yt(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(const s of e.reactions){const i=s.f;(i&b)!==0?yt(s,t,n,r):(i&(pe|M))!==0&&(i&T)===0&&ze(s,t,r)&&(y(s,T),Ge(s))}}function ze(e,t,n){const r=n.get(e);if(r!==void 0)return r;if(e.deps!==null)for(const s of e.deps){if(ie.call(t,s))return!0;if((s.f&b)!==0&&ze(s,t,n))return n.set(s,!0),!0}return n.set(e,!1),!1}function Ge(e){w.schedule(e)}function gt(e,t){if(!((e.f&F)!==0&&(e.f&g)!==0)){(e.f&T)!==0?t.d.push(e):(e.f&j)!==0&&t.m.push(e),y(e,g);for(var n=e.first;n!==null;)gt(n,t),n=n.next}}function mt(e){y(e,g);for(var t=e.first;t!==null;)mt(t),t=t.next}function mn(e,t,n,r){const s=me()?Ke:Tn;var i=e.filter(_=>!_.settled);if(n.length===0&&i.length===0){r(t.map(s));return}var o=v,l=En(),f=i.length===1?i[0].promise:i.length>1?Promise.all(i.map(_=>_.promise)):null;function u(_){l();try{r(_)}catch(p){(o.f&C)===0&&Re(p,o)}ke()}if(n.length===0){f.then(()=>u(t.map(s)));return}var a=Et();function c(){Promise.all(n.map(_=>bn(_))).then(_=>u([...t.map(s),..._])).catch(_=>Re(_,o)).finally(()=>a())}f?f.then(()=>{l(),c(),ke()}):c()}function En(){var e=v,t=d,n=E,r=w;return function(i=!0){ae(e),V(t),Ae(n),i&&(e.f&C)===0&&(r?.activate(),r?.apply())}}function ke(e=!0){ae(null),V(null),Ae(null),e&&w?.deactivate()}function Et(){var e=v,t=e.b,n=w,r=t.is_rendered();return t.update_pending_count(1,n),n.increment(r,e),(s=!1)=>{t.update_pending_count(-1,n),n.decrement(r,e,s)}}function Ke(e){var t=b|T,n=d!==null&&(d.f&b)!==0?d:null;return v!==null&&(v.f|=ge),{ctx:E,deps:null,effects:null,equals:dt,f:t,fn:e,reactions:null,rv:0,v:m,wv:0,parent:n??v,ac:null}}function bn(e,t,n){let r=v;r===null&&Jt();var s=void 0,i=We(m),o=!d,l=new Map;return In(()=>{var f=v,u=ot();s=u.promise;try{Promise.resolve(e()).then(u.resolve,u.reject).finally(ke)}catch(p){u.reject(p),ke()}var a=w;if(o){if((f.f&re)!==0)var c=Et();if(r.b.is_rendered())l.get(a)?.reject(q),l.delete(a);else{for(const p of l.values())p.reject(q);l.clear()}l.set(a,u)}const _=(p,h=void 0)=>{if(c){var N=h===q;c(N)}if(!(h===q||(f.f&C)!==0)){if(a.activate(),h)i.f|=H,Ye(i,h);else{(i.f&H)!==0&&(i.f^=H),Ye(i,p);for(const[ue,be]of l){if(l.delete(ue),ue===a)break;be.reject(q)}}a.deactivate()}};u.promise.then(_,p=>_(null,p||"unknown"))}),Pn(()=>{for(const f of l.values())f.reject(q)}),new Promise(f=>{function u(a){function c(){a===s?f(i):u(s)}a.then(c,c)}u(s)})}function hr(e){const t=Ke(e);return Mt(t),t}function Tn(e){const t=Ke(e);return t.equals=vt,t}function xn(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n0&&!xt&&Rn()}return t}function Rn(){xt=!1;for(const e of Le)(e.f&g)!==0&&y(e,j),Ee(e)&&oe(e);Le.clear()}function Ce(e){K(e,e.v+1)}function St(e,t,n){var r=e.reactions;if(r!==null)for(var s=me(),i=r.length,o=0;o{if(Z===i)return l();var f=d,u=Z;V(null),ft(i);var a=l();return V(f),ft(u),a};return r&&n.set("length",U(e.length)),new Proxy(e,{defineProperty(l,f,u){(!("value"in u)||u.configurable===!1||u.enumerable===!1||u.writable===!1)&&rn();var a=n.get(f);return a===void 0?o(()=>{var c=U(u.value);return n.set(f,c),c}):K(a,u.value,!0),!0},deleteProperty(l,f){var u=n.get(f);if(u===void 0){if(f in l){const a=o(()=>U(m));n.set(f,a),Ce(s)}}else K(u,m),Ce(s);return!0},get(l,f,u){if(f===ve)return e;var a=n.get(f),c=f in l;if(a===void 0&&(!c||de(l,f)?.writable)&&(a=o(()=>{var p=ce(c?l[f]:m),h=U(p);return h}),n.set(f,a)),a!==void 0){var _=_e(a);return _===m?void 0:_}return Reflect.get(l,f,u)},getOwnPropertyDescriptor(l,f){var u=Reflect.getOwnPropertyDescriptor(l,f);if(u&&"value"in u){var a=n.get(f);a&&(u.value=_e(a))}else if(u===void 0){var c=n.get(f),_=c?.v;if(c!==void 0&&_!==m)return{enumerable:!0,configurable:!0,value:_,writable:!0}}return u},has(l,f){if(f===ve)return!0;var u=n.get(f),a=u!==void 0&&u.v!==m||Reflect.has(l,f);if(u!==void 0||v!==null&&(!a||de(l,f)?.writable)){u===void 0&&(u=o(()=>{var _=a?ce(l[f]):m,p=U(_);return p}),n.set(f,u));var c=_e(u);if(c===m)return!1}return a},set(l,f,u,a){var c=n.get(f),_=f in l;if(r&&f==="length")for(var p=u;pU(m)),n.set(p+"",h))}if(c===void 0)(!_||de(l,f)?.writable)&&(c=o(()=>U(void 0)),K(c,ce(u)),n.set(f,c));else{_=c.v!==m;var N=o(()=>ce(u));K(c,N)}var ue=Reflect.getOwnPropertyDescriptor(l,f);if(ue?.set&&ue.set.call(a,u),!_){if(r&&typeof f=="string"){var be=n.get("length"),Pe=Number(f);Number.isInteger(Pe)&&Pe>=be.v&&K(be,Pe+1)}Ce(s)}return!0},ownKeys(l){_e(s);var f=Reflect.ownKeys(l).filter(c=>{var _=n.get(c);return _===void 0||_.v!==m});for(var[u,a]of n)a.v!==m&&!(u in l)&&f.push(u);return f},setPrototypeOf(){sn()}})}var st,kn,On,At,Rt;function wr(){if(st===void 0){st=window,kn=document,On=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;At=de(t,"firstChild").get,Rt=de(t,"nextSibling").get,Qe(e)&&(e.__click=void 0,e.__className=void 0,e.__attributes=null,e.__style=void 0,e.__e=void 0),Qe(n)&&(n.__t=void 0)}}function Oe(e=""){return document.createTextNode(e)}function qe(e){return At.call(e)}function z(e){return Rt.call(e)}function yr(e,t){if(!ee)return qe(e);var n=qe(S);if(n===null)n=S.appendChild(Oe());else if(t&&n.nodeType!==Ne){var r=Oe();return n?.before(r),fe(r),r}return t&&Xe(n),fe(n),n}function gr(e,t=!1){if(!ee){var n=qe(e);return n instanceof Comment&&n.data===""?z(n):n}if(t){if(S?.nodeType!==Ne){var r=Oe();return S?.before(r),fe(r),r}Xe(S)}return S}function mr(e,t=1,n=!1){let r=ee?S:e;for(var s;t--;)s=r,r=z(r);if(!ee)return r;if(n){if(r?.nodeType!==Ne){var i=Oe();return r===null?s?.after(i):r.before(i),fe(i),i}Xe(r)}return fe(r),r}function Er(e){e.textContent=""}function br(){return!1}function Tr(e,t,n){return document.createElementNS(un,e,void 0)}function Xe(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===Ne;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}let it=!1;function Nn(){it||(it=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(const t of e.target.elements)t.__on_r?.()})},{capture:!0}))}function Ze(e){var t=d,n=v;V(null),ae(null);try{return e()}finally{V(t),ae(n)}}function xr(e,t,n,r=n){e.addEventListener(t,()=>Ze(n));const s=e.__on_r;s?e.__on_r=()=>{s(),r(!0)}:e.__on_r=()=>r(!0),Nn()}function kt(e){v===null&&(d===null&&tn(),en()),ne&&Qt()}function Dn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function L(e,t){var n=v;n!==null&&(n.f&I)!==0&&(e|=I);var r={ctx:E,deps:null,nodes:null,f:e|T|k,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};w?.register_created_effect(r);var s=r;if((e&le)!==0)se!==null?se.push(r):B.ensure().schedule(r);else if(t!==null){try{oe(r)}catch(o){throw te(r),o}s.deps===null&&s.teardown===null&&s.nodes===null&&s.first===s.last&&(s.f&ge)===0&&(s=s.first,(e&M)!==0&&(e&Se)!==0&&s!==null&&(s.f|=Se))}if(s!==null&&(s.parent=n,n!==null&&Dn(s,n),d!==null&&(d.f&b)!==0&&(e&J)===0)){var i=d;(i.effects??=[]).push(s)}return r}function Ot(){return d!==null&&!P}function Pn(e){const t=L(ye,null);return y(t,g),t.teardown=e,t}function Sr(e){kt();var t=v.f,n=!d&&(t&F)!==0&&(t&re)===0;if(n){var r=E;(r.e??=[]).push(e)}else return Nt(e)}function Nt(e){return L(le|ct,e)}function Ar(e){return kt(),L(ye|ct,e)}function Rr(e){B.ensure();const t=L(J|ge,e);return(n={})=>new Promise(r=>{n.outro?Fn(t,()=>{te(t),r(void 0)}):(te(t),r(void 0))})}function kr(e){return L(le,e)}function In(e){return L(pe|ge,e)}function Or(e,t=0){return L(ye|t,e)}function Nr(e,t=[],n=[],r=[]){mn(r,t,n,s=>{L(ye,()=>e(...s.map(_e)))})}function Dr(e,t=0){var n=L(M|t,e);return n}function Pr(e){return L(F|ge,e)}function Dt(e){var t=e.teardown;if(t!==null){const n=ne,r=d;lt(!0),V(null);try{t.call(null)}finally{lt(n),V(r)}}}function Je(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){const s=n.ac;s!==null&&Ze(()=>{s.abort(q)});var r=n.next;(n.f&J)!==0?n.parent=null:te(n,t),n=r}}function Cn(e){for(var t=e.first;t!==null;){var n=t.next;(t.f&F)===0&&te(t),t=n}}function te(e,t=!0){var n=!1;(t||(e.f&Xt)!==0)&&e.nodes!==null&&e.nodes.end!==null&&(Mn(e.nodes.start,e.nodes.end),n=!0),y(e,et),Je(e,t&&!n),we(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(const i of r)i.stop();Dt(e),e.f^=et,e.f|=C;var s=e.parent;s!==null&&s.first!==null&&Pt(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Mn(e,t){for(;e!==null;){var n=e===t?null:z(e);e.remove(),e=n}}function Pt(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Fn(e,t,n=!0){var r=[];It(e,r,!0);var s=()=>{n&&te(e),t&&t()},i=r.length;if(i>0){var o=()=>--i||s();for(var l of r)l.out(o)}else s()}function It(e,t,n){if((e.f&I)===0){e.f^=I;var r=e.nodes&&e.nodes.t;if(r!==null)for(const l of r)(l.is_global||n)&&t.push(l);for(var s=e.first;s!==null;){var i=s.next,o=(s.f&Se)!==0||(s.f&F)!==0&&(e.f&M)!==0;It(s,t,o?n:!1),s=i}}}function Ir(e){Ct(e,!0)}function Ct(e,t){if((e.f&I)!==0){e.f^=I,(e.f&g)===0&&(y(e,T),B.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,s=(n.f&Se)!==0||(n.f&F)!==0;Ct(n,s?t:!1),n=r}var i=e.nodes&&e.nodes.t;if(i!==null)for(const o of i)(o.is_global||t)&&o.in()}}function Cr(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var s=n===r?null:z(n);t.append(n),n=s}}let xe=!1,ne=!1;function lt(e){ne=e}let d=null,P=!1;function V(e){d=e}let v=null;function ae(e){v=e}let O=null;function Mt(e){d!==null&&(O===null?O=[e]:O.push(e))}let x=null,A=0,R=null;function jn(e){R=e}let Ft=1,W=0,Z=W;function ft(e){Z=e}function jt(){return++Ft}function Ee(e){var t=e.f;if((t&T)!==0)return!0;if(t&b&&(e.f&=~Q),(t&j)!==0){for(var n=e.deps,r=n.length,s=0;se.wv)return!0}(t&k)!==0&&D===null&&y(e,g)}return!1}function Lt(e,t,n=!0){var r=e.reactions;if(r!==null&&!(O!==null&&ie.call(O,e)))for(var s=0;s{e.ac.abort(q)}),e.ac=null);try{e.f|=Fe;var a=e.fn,c=a();e.f|=re;var _=e.deps,p=w?.is_fork;if(x!==null){var h;if(p||we(e,A),_!==null&&A>0)for(_.length=A+x.length,h=0;h{const n=a(e);if(typeof n=="function")return n})}function c(e){var n=e.l;return n.u??={a:[],b:[],m:[]}}export{s as o}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/CcgZgiL9.js - -```js -import{U as re,g as G,y as ne,V as J,i as se,W as Y,q as N,J as u,m as _,X as g,Y as q,z as ae,Z as X,_ as ie,a0 as z,a1 as m,A as R,a2 as F,w as U,a3 as oe,a4 as fe,a5 as M,a6 as L,a7 as $,a8 as le,a9 as he,aa as K,e as Z,ab as de,ac as C,G as S,n as ue,ad as ce,ae as A,E as _e,B as pe,af as ve,ag as ge,ah as ye,ai as me,aj as Te,ak as Ee,K as I,al as be,am as we,an as Ne,ao as Re,ap as Se,aq as Ae,ar as k,C as Q,as as De,D as Oe,at as P,F as D,au as Me,av as Le,aw as xe,ax as Fe,p as Ce,ay as Ie,az as ke,a as Pe}from"./3TW-MQft.js";function He(t){let e=0,r=J(0),s;return()=>{re()&&(G(r),ne(()=>(e===0&&(s=se(()=>t(()=>Y(r)))),e+=1,()=>{N(()=>{e-=1,e===0&&(s?.(),s=void 0,Y(r))})})))}}var Be=_e|pe;function Ve(t,e,r,s){new We(t,e,r,s)}class We{parent;is_pending=!1;transform_error;#t;#_=_?u:null;#s;#l;#e;#a=null;#r=null;#n=null;#i=null;#h=0;#f=0;#d=!1;#p=new Set;#v=new Set;#o=null;#m=He(()=>(this.#o=J(this.#h),()=>{this.#o=null}));constructor(e,r,s,a){this.#t=e,this.#s=r,this.#l=n=>{var i=g;i.b=this,i.f|=q,s(n)},this.parent=g.b,this.transform_error=a??this.parent?.transform_error??(n=>n),this.#e=ae(()=>{if(_){const n=this.#_;X();const i=n.data===ie;if(n.data.startsWith(z)){const o=JSON.parse(n.data.slice(z.length));this.#E(o)}else i?this.#b():this.#T()}else this.#g()},Be),_&&(this.#t=u)}#T(){try{this.#a=m(()=>this.#l(this.#t))}catch(e){this.error(e)}}#E(e){const r=this.#s.failed;r&&(this.#n=m(()=>{r(this.#t,()=>e,()=>()=>{})}))}#b(){const e=this.#s.pending;e&&(this.is_pending=!0,this.#r=m(()=>e(this.#t)),N(()=>{var r=this.#i=document.createDocumentFragment(),s=R();r.append(s),this.#a=this.#c(()=>m(()=>this.#l(s))),this.#f===0&&(this.#t.before(r),this.#i=null,F(this.#r,()=>{this.#r=null}),this.#u(U))}))}#g(){try{if(this.is_pending=this.has_pending_snippet(),this.#f=0,this.#h=0,this.#a=m(()=>{this.#l(this.#t)}),this.#f>0){var e=this.#i=document.createDocumentFragment();oe(this.#a,e);const r=this.#s.pending;this.#r=m(()=>r(this.#t))}else this.#u(U)}catch(r){this.error(r)}}#u(e){this.is_pending=!1,e.transfer_effects(this.#p,this.#v)}defer_effect(e){fe(e,this.#p,this.#v)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#s.pending}#c(e){var r=g,s=K,a=Z;M(this.#e),L(this.#e),$(this.#e.ctx);try{return le.ensure(),e()}catch(n){return he(n),null}finally{M(r),L(s),$(a)}}#y(e,r){if(!this.has_pending_snippet()){this.parent&&this.parent.#y(e,r);return}this.#f+=e,this.#f===0&&(this.#u(r),this.#r&&F(this.#r,()=>{this.#r=null}),this.#i&&(this.#t.before(this.#i),this.#i=null))}update_pending_count(e,r){this.#y(e,r),this.#h+=e,!(!this.#o||this.#d)&&(this.#d=!0,N(()=>{this.#d=!1,this.#o&&de(this.#o,this.#h)}))}get_effect_pending(){return this.#m(),G(this.#o)}error(e){var r=this.#s.onerror;let s=this.#s.failed;if(!r&&!s)throw e;this.#a&&(C(this.#a),this.#a=null),this.#r&&(C(this.#r),this.#r=null),this.#n&&(C(this.#n),this.#n=null),_&&(S(this.#_),ue(),S(ce()));var a=!1,n=!1;const i=()=>{if(a){ge();return}a=!0,n&&ve(),this.#n!==null&&F(this.#n,()=>{this.#n=null}),this.#c(()=>{this.#g()})},c=o=>{try{n=!0,r?.(o,i),n=!1}catch(f){A(f,this.#e&&this.#e.parent)}s&&(this.#n=this.#c(()=>{try{return m(()=>{var f=g;f.b=this,f.f|=q,s(this.#t,()=>o,()=>i)})}catch(f){return A(f,this.#e.parent),null}}))};N(()=>{var o;try{o=this.transform_error(e)}catch(f){A(f,this.#e&&this.#e.parent);return}o!==null&&typeof o=="object"&&typeof o.then=="function"?o.then(c,f=>A(f,this.#e&&this.#e.parent)):c(o)})}}const Ye=["touchstart","touchmove"];function qe(t){return Ye.includes(t)}const w=Symbol("events"),ee=new Set,H=new Set;function ze(t,e,r,s={}){function a(n){if(s.capture||B.call(e,n),!n.cancelBubble)return Te(()=>r?.call(this,n))}return t.startsWith("pointer")||t.startsWith("touch")||t==="wheel"?N(()=>{e.addEventListener(t,a,s)}):e.addEventListener(t,a,s),a}function Ke(t,e,r,s,a){var n={capture:s,passive:a},i=ze(t,e,r,n);(e===document.body||e===window||e===document||e instanceof HTMLMediaElement)&&me(()=>{e.removeEventListener(t,i,n)})}function Ze(t,e,r){(e[w]??={})[t]=r}function Qe(t){for(var e=0;e{throw p});throw v}}finally{t[w]=e,delete t.currentTarget,L(T),M(b)}}}const Ue=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy("svelte-trusted-html",{createHTML:t=>t});function $e(t){return Ue?.createHTML(t)??t}function je(t){var e=Ee("template");return e.innerHTML=$e(t.replaceAll("","")),e.content}function y(t,e){var r=g;r.nodes===null&&(r.nodes={start:t,end:e,a:null,t:null})}function et(t,e){var r=(e&we)!==0,s=(e&Ne)!==0,a,n=!t.startsWith("");return()=>{if(_)return y(u,null),u;a===void 0&&(a=je(n?t:""+t),r||(a=I(a)));var i=s||be?document.importNode(a,!0):a.cloneNode(!0);if(r){var c=I(i),o=i.lastChild;y(c,o)}else y(i,i);return i}}function tt(t=""){if(!_){var e=R(t+"");return y(e,e),e}var r=u;return r.nodeType!==Se?(r.before(r=R()),S(r)):Ae(r),y(r,r),r}function rt(){if(_)return y(u,null),u;var t=document.createDocumentFragment(),e=document.createComment(""),r=R();return t.append(e,r),y(e,r),t}function nt(t,e){if(_){var r=g;((r.f&Re)===0||r.nodes.end===null)&&(r.nodes.end=u),X();return}t!==null&&t.before(e)}function st(t,e){var r=e==null?"":typeof e=="object"?`${e}`:e;r!==(t.__t??=t.nodeValue)&&(t.__t=r,t.nodeValue=`${r}`)}function Ge(t,e){return te(t,e)}function at(t,e){k(),e.intro=e.intro??!1;const r=e.target,s=_,a=u;try{for(var n=I(r);n&&(n.nodeType!==Q||n.data!==De);)n=Oe(n);if(!n)throw P;D(!0),S(n);const i=te(t,{...e,anchor:n});return D(!1),i}catch(i){if(i instanceof Error&&i.message.split(` -`).some(c=>c.startsWith("https://svelte.dev/e/")))throw i;return i!==P&&console.warn("Failed to hydrate: ",i),e.recover===!1&&Me(),k(),Le(r),D(!1),Ge(t,e)}finally{D(s),S(a)}}const O=new Map;function te(t,{target:e,anchor:r,props:s={},events:a,context:n,intro:i=!0,transformError:c}){k();var o=void 0,f=xe(()=>{var T=r??e.appendChild(R());Ve(T,{pending:()=>{}},h=>{Ce({});var l=Z;if(n&&(l.c=n),a&&(s.$$events=a),_&&y(h,null),o=t(h,s)||{},_&&(g.nodes.end=u,u===null||u.nodeType!==Q||u.data!==Ie))throw ke(),P;Pe()},c);var b=new Set,v=h=>{for(var l=0;l{for(var h of b)for(const p of[e,document]){var l=O.get(p),d=l.get(h);--d==0?(p.removeEventListener(h,B),l.delete(h),l.size===0&&O.delete(p)):l.set(h,d)}H.delete(v),T!==r&&T.parentNode?.removeChild(T)}});return V.set(o,f),o}let V=new WeakMap;function it(t,e){const r=V.get(t);return r?(V.delete(t),r(e)):Promise.resolve()}const Je="5";typeof window<"u"&&((window.__svelte??={}).v??=new Set).add(Je);export{nt as a,Ze as b,rt as c,Qe as d,Ke as e,et as f,at as h,Ge as m,st as s,tt as t,it as u}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/CifMVRjc.js - -```js -import{_ as x,a as z,g as A}from"./HWAjEwF5.js";var C=10240,D=["image/","audio/","video/","application/octet-stream","font/"];function q(e){return e?D.some(function(n){return e.toLowerCase().startsWith(n)}):!1}function N(e){if(e!=null){if(typeof e=="string")return e;if(e instanceof URLSearchParams)return e.toString();if(e instanceof FormData){var n=[];return e.forEach(function(t,i){n.push("".concat(i,"=").concat(typeof t=="string"?t:"[File]"))}),n.join("&")}}}function k(e,n){if(new Blob([e]).size<=n)return{value:e,truncated:!1};for(var t=0,i=Math.min(e.length,n);t0&&e.charCodeAt(t-1)>=55296&&e.charCodeAt(t-1)<=56319&&(t-=1),{value:e.slice(0,t),truncated:!0}}var R=(function(){function e(){this.fetchObserver=null}return e.prototype.start=function(n,t){this.eventCallback=n,this.networkConfig=t,this.observeFetch()},e.prototype.stop=function(){var n;(n=this.fetchObserver)===null||n===void 0||n.call(this),this.fetchObserver=null,this.eventCallback=void 0,this.networkConfig=void 0},e.prototype.notifyEvent=function(n){var t;(t=this.eventCallback)===null||t===void 0||t.call(this,n)},e.prototype.observeFetch=function(){var n=this,t=A();if(t){var i=t.fetch;i&&(t.fetch=function(s,o){return x(n,void 0,void 0,function(){var d,r,a,h,b,u,l,v,g,w,p,l,y,B=this,m,E;return z(this,function(f){switch(f.label){case 0:d=Date.now(),r={timestamp:d,type:"fetch",method:o?.method||"GET",url:s.toString(),requestHeaders:o?.headers},a=(m=this.networkConfig)===null||m===void 0?void 0:m.body,a?.request&&(h=N(o?.body),h!==void 0&&(b=(E=a.maxBodySizeBytes)!==null&&E!==void 0?E:C,r.requestBody=k(h,b).value)),f.label=1;case 1:return f.trys.push([1,3,,4]),[4,i(s,o)];case 2:return u=f.sent(),l=Date.now(),r.status=u.status,r.duration=l-d,v={},u.headers.forEach(function(_,c){v[c]=_}),r.responseHeaders=v,a?.response?(g=v["content-type"]||null,q(g)?(r.responseBodyStatus="skipped_binary",this.notifyEvent(r)):(w=u.clone(),w.text().then(function(_){var c,T=(c=a.maxBodySizeBytes)!==null&&c!==void 0?c:C,S=k(_,T),O=S.value,F=S.truncated;r.responseBody=O,r.responseBodyStatus=F?"truncated":"captured",B.notifyEvent(r)},function(){r.responseBodyStatus="error",B.notifyEvent(r)}))):this.notifyEvent(r),[2,u];case 3:throw p=f.sent(),l=Date.now(),r.duration=l-d,y=p,r.error={name:y.name||"UnknownError",message:y.message||"An unknown error occurred"},this.notifyEvent(r),p;case 4:return[2]}})})},this.fetchObserver=function(){t.fetch=i})}},e})();export{R as NetworkObservers}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/CpUSghGZ.js - -```js -import{aU as me,aV as wt,b as U,g as A,d as T,x as ne,aW as vt}from"./3TW-MQft.js";import{o as Be}from"./CahBYLSE.js";const D=[];function xe(e,t=me){let n=null;const r=new Set;function a(o){if(wt(e,o)&&(e=o,n)){const l=!D.length;for(const c of r)c[1](),D.push(c,e);if(l){for(let c=0;c{r.delete(c),r.size===0&&n&&(n(),n=null)}}return{set:a,update:i,subscribe:s}}class Le{constructor(t,n){this.status=t,typeof n=="string"?this.body={message:n}:n?this.body=n:this.body={message:`Error: ${t}`}}toString(){return JSON.stringify(this.body)}}class Ue{constructor(t,n){this.status=t,this.location=n}}class Ae extends Error{constructor(t,n,r){super(r),this.status=t,this.text=n}}new URL("sveltekit-internal://");function yt(e,t){return e==="/"||t==="ignore"?e:t==="never"?e.endsWith("/")?e.slice(0,-1):e:t==="always"&&!e.endsWith("/")?e+"/":e}function bt(e){return e.split("%25").map(decodeURI).join("%25")}function kt(e){for(const t in e)e[t]=decodeURIComponent(e[t]);return e}function we({href:e}){return e.split("#")[0]}function Et(...e){let t=5381;for(const n of e)if(typeof n=="string"){let r=n.length;for(;r;)t=t*33^n.charCodeAt(--r)}else if(ArrayBuffer.isView(n)){const r=new Uint8Array(n.buffer,n.byteOffset,n.byteLength);let a=r.length;for(;a;)t=t*33^r[--a]}else throw new TypeError("value must be a string or TypedArray");return(t>>>0).toString(36)}new TextEncoder;new TextDecoder;function St(e){const t=atob(e),n=new Uint8Array(t.length);for(let r=0;r((e instanceof Request?e.method:t?.method||"GET")!=="GET"&&G.delete(Te(e)),Rt(e,t));const G=new Map;function xt(e,t){const n=Te(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:a,...i}=JSON.parse(r.textContent);const s=r.getAttribute("data-ttl");return s&&G.set(n,{body:a,init:i,ttl:1e3*Number(s)}),r.getAttribute("data-b64")!==null&&(a=St(a)),Promise.resolve(new Response(a,i))}return window.fetch(e,t)}function Lt(e,t,n){if(G.size>0){const r=Te(e,n),a=G.get(r);if(a){if(performance.now(){const a=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(r);if(a)return t.push({name:a[1],matcher:a[2],optional:!1,rest:!0,chained:!0}),"(?:/([^]*))?";const i=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(r);if(i)return t.push({name:i[1],matcher:i[2],optional:!0,rest:!1,chained:!0}),"(?:/([^/]+))?";if(!r)return;const s=r.split(/\[(.+?)\](?!\])/);return"/"+s.map((l,c)=>{if(c%2){if(l.startsWith("x+"))return ve(String.fromCharCode(parseInt(l.slice(2),16)));if(l.startsWith("u+"))return ve(String.fromCharCode(...l.slice(2).split("-").map(_=>parseInt(_,16))));const f=Ut.exec(l),[,p,w,h,u]=f;return t.push({name:h,matcher:u,optional:!!p,rest:!!w,chained:w?c===1&&s[0]==="":!1}),w?"([^]*?)":p?"([^/]*)?":"([^/]+?)"}return ve(l)}).join("")}).join("")}/?$`),params:t}}function Tt(e){return e!==""&&!/^\([^)]+\)$/.test(e)}function It(e){return e.slice(1).split("/").filter(Tt)}function Ot(e,t,n){const r={},a=e.slice(1),i=a.filter(o=>o!==void 0);let s=0;for(let o=0;of).join("/"),s=0),c===void 0)if(l.rest)c="";else continue;if(!l.matcher||n[l.matcher](c)){r[l.name]=c;const f=t[o+1],p=a[o+1];f&&!f.rest&&f.optional&&p&&l.chained&&(s=0),!f&&!p&&Object.keys(r).length===i.length&&(s=0);continue}if(l.optional&&l.chained){s++;continue}return}if(!s)return r}function ve(e){return e.normalize().replace(/[[\]]/g,"\\$&").replace(/%/g,"%25").replace(/\//g,"%2[Ff]").replace(/\?/g,"%3[Ff]").replace(/#/g,"%23").replace(/[.*+?^${}()|\\]/g,"\\$&")}function Pt({nodes:e,server_loads:t,dictionary:n,matchers:r}){const a=new Set(t);return Object.entries(n).map(([o,[l,c,f]])=>{const{pattern:p,params:w}=At(o),h={id:o,exec:u=>{const _=p.exec(u);if(_)return Ot(_,w,r)},errors:[1,...f||[]].map(u=>e[u]),layouts:[0,...c||[]].map(s),leaf:i(l)};return h.errors.length=h.layouts.length=Math.max(h.errors.length,h.layouts.length),h});function i(o){const l=o<0;return l&&(o=~o),[l,e[o]]}function s(o){return o===void 0?o:[a.has(o),e[o]]}}function Je(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function Ke(e,t,n=JSON.stringify){const r=n(t);try{sessionStorage[e]=r}catch{}}const L=globalThis.__sveltekit_1bdh8ft?.base??"",$t=globalThis.__sveltekit_1bdh8ft?.assets??L??"",Ct="1775593135145",Xe="sveltekit:snapshot",Qe="sveltekit:scroll",Ze="sveltekit:states",jt="sveltekit:pageurl",B="sveltekit:history",H="sveltekit:navigation",j={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},de=location.origin;function Ie(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){const n=document.getElementsByTagName("base");t=n.length?n[0].href:document.URL}return new URL(e,t)}function q(){return{x:pageXOffset,y:pageYOffset}}function V(e,t){return e.getAttribute(`data-sveltekit-${t}`)}const Me={...j,"":j.hover};function et(e){let t=e.assignedSlot??e.parentNode;return t?.nodeType===11&&(t=t.host),t}function tt(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()==="A"&&e.hasAttribute("href"))return e;e=et(e)}}function ke(e,t,n){let r;try{if(r=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&r.hash.match(/^#[^/]/)){const o=location.hash.split("#")[1]||"/";r.hash=`#${o}${r.hash}`}}catch{}const a=e instanceof SVGAElement?e.target.baseVal:e.target,i=!r||!!a||he(r,t,n)||(e.getAttribute("rel")||"").split(/\s+/).includes("external"),s=r?.origin===de&&e.hasAttribute("download");return{url:r,external:i,target:a,download:s}}function re(e){let t=null,n=null,r=null,a=null,i=null,s=null,o=e;for(;o&&o!==document.documentElement;)r===null&&(r=V(o,"preload-code")),a===null&&(a=V(o,"preload-data")),t===null&&(t=V(o,"keepfocus")),n===null&&(n=V(o,"noscroll")),i===null&&(i=V(o,"reload")),s===null&&(s=V(o,"replacestate")),o=et(o);function l(c){switch(c){case"":case"true":return!0;case"off":case"false":return!1;default:return}}return{preload_code:Me[r??"off"],preload_data:Me[a??"off"],keepfocus:l(t),noscroll:l(n),reload:l(i),replace_state:l(s)}}function We(e){const t=xe(e);let n=!0;function r(){n=!0,t.update(s=>s)}function a(s){n=!1,t.set(s)}function i(s){let o;return t.subscribe(l=>{(o===void 0||n&&l!==o)&&s(o=l)})}return{notify:r,set:a,subscribe:i}}const nt={v:()=>{}};function Nt(){const{set:e,subscribe:t}=xe(!1);let n;async function r(){clearTimeout(n);try{const a=await fetch(`${$t}/_app/version.json`,{headers:{pragma:"no-cache","cache-control":"no-cache"}});if(!a.ok)return!1;const s=(await a.json()).version!==Ct;return s&&(e(!0),nt.v(),clearTimeout(n)),s}catch{return!1}}return{subscribe:t,check:r}}function he(e,t,n){return e.origin!==de||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function fn(e){}const rt=new Set(["load","prerender","csr","ssr","trailingSlash","config"]);[...rt];const qt=new Set([...rt]);[...qt];function Dt(e){return e.filter(t=>t!=null)}function Oe(e){return e instanceof Le||e instanceof Ae?e.status:500}function Vt(e){return e instanceof Ae?e.text:"Internal Error"}let E,J,ye;const Bt=Be.toString().includes("$$")||/function \w+\(\) \{\}/.test(Be.toString()),ze="a:";Bt?(E={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(ze)},J={current:null},ye={current:!1}):(E=new class{#e=U({});get data(){return A(this.#e)}set data(t){T(this.#e,t)}#t=U(null);get form(){return A(this.#t)}set form(t){T(this.#t,t)}#n=U(null);get error(){return A(this.#n)}set error(t){T(this.#n,t)}#r=U({});get params(){return A(this.#r)}set params(t){T(this.#r,t)}#a=U({id:null});get route(){return A(this.#a)}set route(t){T(this.#a,t)}#o=U({});get state(){return A(this.#o)}set state(t){T(this.#o,t)}#s=U(-1);get status(){return A(this.#s)}set status(t){T(this.#s,t)}#i=U(new URL(ze));get url(){return A(this.#i)}set url(t){T(this.#i,t)}},J=new class{#e=U(null);get current(){return A(this.#e)}set current(t){T(this.#e,t)}},ye=new class{#e=U(!1);get current(){return A(this.#e)}set current(t){T(this.#e,t)}},nt.v=()=>ye.current=!0);function Kt(e){Object.assign(E,e)}const Mt=new Set(["icon","shortcut icon","apple-touch-icon"]);let z=null;const C=Je(Qe)??{},X=Je(Xe)??{},$={url:We({}),page:We({}),navigating:xe(null),updated:Nt()};function Pe(e){C[e]=q()}function Wt(e,t){let n=e+1;for(;C[n];)delete C[n],n+=1;for(n=t+1;X[n];)delete X[n],n+=1}function Q(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(()=>{})}async function at(){if("serviceWorker"in navigator){const e=await navigator.serviceWorker.getRegistration(L||"/");e&&await e.update()}}function Fe(){}let $e,Ee,ae,I,Se,y;const oe=[],se=[];let R=null;function ie(){R?.fork?.then(e=>e?.discard()),R=null}const te=new Map,ot=new Set,zt=new Set,Y=new Set;let m={branch:[],error:null,url:null},st=!1,le=!1,Ge=!0,Z=!1,F=!1,it=!1,Ce=!1,lt,v,x,N;const ce=new Set,Ye=new Map;async function gn(e,t,n){globalThis.__sveltekit_1bdh8ft&&(globalThis.__sveltekit_1bdh8ft.query,globalThis.__sveltekit_1bdh8ft.prerender),document.URL!==location.href&&(location.href=location.href),y=e,await e.hooks.init?.(),$e=Pt(e),I=document.documentElement,Se=t,Ee=e.nodes[0],ae=e.nodes[1],Ee(),ae(),v=history.state?.[B],x=history.state?.[H],v||(v=x=Date.now(),history.replaceState({...history.state,[B]:v,[H]:x},""));const r=C[v];function a(){r&&(history.scrollRestoration="manual",scrollTo(r.x,r.y))}n?(a(),await an(Se,n)):(await K({type:"enter",url:Ie(y.hash?ln(new URL(location.href)):location.href),replace_state:!0}),a()),rn()}function Ft(){oe.length=0,Ce=!1}function ct(e){se.some(t=>t?.snapshot)&&(X[e]=se.map(t=>t?.snapshot?.capture()))}function ut(e){X[e]?.forEach((t,n)=>{se[n]?.snapshot?.restore(t)})}function He(){Pe(v),Ke(Qe,C),ct(x),Ke(Xe,X)}async function ft(e,t,n,r){let a;t.invalidateAll&&ie(),await K({type:"goto",url:Ie(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:r,accept:()=>{t.invalidateAll&&(Ce=!0,a=[],Ye.forEach((i,s)=>{for(const o of i.keys())a.push(s+"/"+o)})),t.invalidate&&t.invalidate.forEach(nn)}}),t.invalidateAll&&ne().then(ne).then(()=>{Ye.forEach((i,s)=>{i.forEach(({resource:o},l)=>{a?.includes(s+"/"+l)&&o.refresh?.()})})})}async function Gt(e){if(e.id!==R?.id){ie();const t={};ce.add(t),R={id:e.id,token:t,promise:ht({...e,preload:t}).then(n=>(ce.delete(t),n.type==="loaded"&&n.state.error&&ie(),n)),fork:null}}return R.promise}async function be(e){const t=(await pe(e,!1))?.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(n=>n[1]()))}async function dt(e,t,n){const r={params:m.params,route:{id:m.route?.id??null},url:new URL(location.href)};m={...e.state,nav:r};const a=document.querySelector("style[data-sveltekit]");if(a&&a.remove(),Object.assign(E,e.props.page),lt=new y.root({target:t,props:{...e.props,stores:$,components:se},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),ut(x),n){const i={from:null,to:{...r,scroll:C[v]??q()},willUnload:!1,type:"enter",complete:Promise.resolve()};Y.forEach(s=>s(i))}le=!0}async function ue({url:e,params:t,branch:n,errors:r,status:a,error:i,route:s,form:o}){let l="never";if(L&&(e.pathname===L||e.pathname===L+"/"))l="always";else for(const u of n)u?.slash!==void 0&&(l=u.slash);e.pathname=yt(e.pathname,l),e.search=e.search;const c={type:"loaded",state:{url:e,params:t,branch:n,error:i,route:s},props:{constructors:Dt(n).map(u=>u.node.component),page:Ve(E)}};o!==void 0&&(c.props.form=o);let f={},p=!E,w=0;for(let u=0;uo(new URL(s))))return!0;return!1}function Ne(e,t){return e?.type==="data"?e:e?.type==="skip"?t??null:null}function Jt(e,t){if(!e)return new Set(t.searchParams.keys());const n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(const r of n){const a=e.searchParams.getAll(r),i=t.searchParams.getAll(r);a.every(s=>i.includes(s))&&i.every(s=>a.includes(s))&&n.delete(r)}return n}function Xt({error:e,url:t,route:n,params:r}){return{type:"loaded",state:{error:e,url:t,route:n,params:r,branch:[]},props:{page:Ve(E),constructors:[]}}}async function ht({id:e,invalidating:t,url:n,params:r,route:a,preload:i}){if(R?.id===e)return ce.delete(R.token),R.promise;const{errors:s,layouts:o,leaf:l}=a,c=[...o,l];s.forEach(g=>g?.().catch(()=>{})),c.forEach(g=>g?.[1]().catch(()=>{}));const f=m.url?e!==fe(m.url):!1,p=m.route?a.id!==m.route.id:!1,w=Jt(m.url,n);let h=!1;const u=c.map(async(g,d)=>{if(!g)return;const b=m.branch[d];return g[1]===b?.loader&&!Ht(h,p,f,w,b.universal?.uses,r)?b:(h=!0,je({loader:g[1],url:n,params:r,route:a,parent:async()=>{const O={};for(let P=0;P{});const _=[];for(let g=0;gPromise.resolve({}),server_data_node:Ne(i)}),o={node:await ae(),loader:ae,universal:null,server:null,data:null};return ue({url:n,params:a,branch:[s,o],status:e,error:t,errors:[],route:null})}catch(s){if(s instanceof Ue)return ft(new URL(s.location,location.href),{},0);throw s}}async function Zt(e){const t=e.href;if(te.has(t))return te.get(t);let n;try{const r=(async()=>{let a=await y.hooks.reroute({url:new URL(e),fetch:async(i,s)=>Yt(i,s,e).promise})??e;if(typeof a=="string"){const i=new URL(e);y.hash?i.hash=a:i.pathname=a,a=i}return a})();te.set(t,r),n=await r}catch{te.delete(t);return}return n}async function pe(e,t){if(e&&!he(e,L,y.hash)){const n=await Zt(e);if(!n)return;const r=en(n);for(const a of $e){const i=a.exec(r);if(i)return{id:fe(e),invalidating:t,route:a,params:kt(i),url:e}}}}function en(e){return bt(y.hash?e.hash.replace(/^#/,"").replace(/[?#].+/,""):e.pathname.slice(L.length))||"/"}function fe(e){return(y.hash?e.hash.replace(/^#/,""):e.pathname)+e.search}function pt({url:e,type:t,intent:n,delta:r,event:a,scroll:i}){let s=!1;const o=De(m,n,e,t,i??null);r!==void 0&&(o.navigation.delta=r),a!==void 0&&(o.navigation.event=a);const l={...o.navigation,cancel:()=>{s=!0,o.reject(new Error("navigation cancelled"))}};return Z||ot.forEach(c=>c(l)),s?null:o}async function K({type:e,url:t,popped:n,keepfocus:r,noscroll:a,replace_state:i,state:s={},redirect_count:o=0,nav_token:l={},accept:c=Fe,block:f=Fe,event:p}){const w=N;N=l;const h=await pe(t,!1),u=e==="enter"?De(m,h,t,e):pt({url:t,type:e,delta:n?.delta,intent:h,scroll:n?.scroll,event:p});if(!u){f(),N===l&&(N=w);return}const _=v,g=x;c(),Z=!0,le&&u.navigation.type!=="enter"&&$.navigating.set(J.current=u.navigation);let d=h&&await ht(h);if(!d){if(he(t,L,y.hash))return await Q(t,i);d=await gt(t,{id:null},await ee(new Ae(404,"Not Found",`Not found: ${t.pathname}`),{url:t,params:{},route:{id:null}}),404,i)}if(t=h?.url||t,N!==l)return u.reject(new Error("navigation aborted")),!1;if(d.type==="redirect"){if(o<20){await K({type:e,url:new URL(d.location,t),popped:n,keepfocus:r,noscroll:a,replace_state:i,state:s,redirect_count:o+1,nav_token:l}),u.fulfil(void 0);return}d=await qe({status:500,error:await ee(new Error("Redirect loop"),{url:t,params:{},route:{id:null}}),url:t,route:{id:null}})}else d.props.page.status>=400&&await $.updated.check()&&(await at(),await Q(t,i));if(Ft(),Pe(_),ct(g),d.props.page.url.pathname!==t.pathname&&(t.pathname=d.props.page.url.pathname),s=n?n.state:s,!n){const k=i?0:1,M={[B]:v+=k,[H]:x+=k,[Ze]:s};(i?history.replaceState:history.pushState).call(history,M,"",t),i||Wt(v,x)}const b=h&&R?.id===h.id?R.fork:null;R?.fork&&!b&&ie(),R=null,d.props.page.state=s;let S;if(le){const k=(await Promise.all(Array.from(zt,W=>W(u.navigation)))).filter(W=>typeof W=="function");if(k.length>0){let W=function(){k.forEach(_e=>{Y.delete(_e)})};k.push(W),k.forEach(_e=>{Y.add(_e)})}const M=u.navigation.to;m={...d.state,nav:{params:M.params,route:M.route,url:M.url}},d.props.page&&(d.props.page.url=t);const ge=b&&await b;ge?S=ge.commit():(z=null,lt.$set(d.props),z&&Object.assign(d.props.page,z),Kt(d.props.page),S=vt?.()),it=!0}else await dt(d,Se,!1);const{activeElement:O}=document;await S,await ne(),await ne();let P=null;if(Ge){const k=n?n.scroll:a?q():null;k?scrollTo(k.x,k.y):(P=t.hash&&document.getElementById(_t(t)))?P.scrollIntoView():scrollTo(0,0)}const mt=document.activeElement!==O&&document.activeElement!==document.body;!r&&!mt&&sn(t,!P),Ge=!0,d.props.page&&(z&&Object.assign(d.props.page,z),Object.assign(E,d.props.page)),Z=!1,e==="popstate"&&ut(x),u.fulfil(void 0),u.navigation.to&&(u.navigation.to.scroll=q()),Y.forEach(k=>k(u.navigation)),$.navigating.set(J.current=null)}async function gt(e,t,n,r,a){return e.origin===de&&e.pathname===location.pathname&&!st?await qe({status:r,error:n,url:e,route:t}):await Q(e,a)}function tn(){let e,t={element:void 0,href:void 0},n;I.addEventListener("mousemove",o=>{const l=o.target;clearTimeout(e),e=setTimeout(()=>{i(l,j.hover)},20)});function r(o){o.defaultPrevented||i(o.composedPath()[0],j.tap)}I.addEventListener("mousedown",r),I.addEventListener("touchstart",r,{passive:!0});const a=new IntersectionObserver(o=>{for(const l of o)l.isIntersecting&&(be(new URL(l.target.href)),a.unobserve(l.target))},{threshold:0});async function i(o,l){const c=tt(o,I),f=c===t.element&&c?.href===t.href&&l>=n;if(!c||f)return;const{url:p,external:w,download:h}=ke(c,L,y.hash);if(w||h)return;const u=re(c),_=p&&fe(m.url)===fe(p);if(!(u.reload||_))if(l<=u.preload_data){t={element:c,href:c.href},n=j.tap;const g=await pe(p,!1);if(!g)return;Gt(g)}else l<=u.preload_code&&(t={element:c,href:c.href},n=l,be(p))}function s(){a.disconnect();for(const o of I.querySelectorAll("a")){const{url:l,external:c,download:f}=ke(o,L,y.hash);if(c||f)continue;const p=re(o);p.reload||(p.preload_code===j.viewport&&a.observe(o),p.preload_code===j.eager&&be(l))}}Y.add(s),s()}function ee(e,t){if(e instanceof Le)return e.body;const n=Oe(e),r=Vt(e);return y.hooks.handleError({error:e,event:t,status:n,message:r})??{message:r}}function _n(e,t={}){return e=new URL(Ie(e)),e.origin!==de?Promise.reject(new Error("goto: invalid URL")):ft(e,t,0)}function nn(e){if(typeof e=="function")oe.push(e);else{const{href:t}=new URL(e,location.href);oe.push(n=>n.href===t)}}function rn(){history.scrollRestoration="manual",addEventListener("beforeunload",t=>{let n=!1;if(He(),!Z){const r=De(m,void 0,null,"leave"),a={...r.navigation,cancel:()=>{n=!0,r.reject(new Error("navigation cancelled"))}};ot.forEach(i=>i(a))}n?(t.preventDefault(),t.returnValue=""):history.scrollRestoration="auto"}),addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&He()}),navigator.connection?.saveData||tn(),I.addEventListener("click",async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;const n=tt(t.composedPath()[0],I);if(!n)return;const{url:r,external:a,target:i,download:s}=ke(n,L,y.hash);if(!r)return;if(i==="_parent"||i==="_top"){if(window.parent!==window)return}else if(i&&i!=="_self")return;const o=re(n);if(!(n instanceof SVGAElement)&&r.protocol!==location.protocol&&!(r.protocol==="https:"||r.protocol==="http:")||s)return;const[c,f]=(y.hash?r.hash.replace(/^#/,""):r.href).split("#"),p=c===we(location);if(a||o.reload&&(!p||!f)){pt({url:r,type:"link",event:t})?Z=!0:t.preventDefault();return}if(f!==void 0&&p){const[,w]=m.url.href.split("#");if(w===f){if(t.preventDefault(),f===""||f==="top"&&n.ownerDocument.getElementById("top")===null)scrollTo({top:0});else{const h=n.ownerDocument.getElementById(decodeURIComponent(f));h&&(h.scrollIntoView(),h.focus())}return}if(F=!0,Pe(v),e(r),!o.replace_state)return;F=!1}t.preventDefault(),await new Promise(w=>{requestAnimationFrame(()=>{setTimeout(w,0)}),setTimeout(w,100)}),await K({type:"link",url:r,keepfocus:o.keepfocus,noscroll:o.noscroll,replace_state:o.replace_state??r.href===location.href,event:t})}),I.addEventListener("submit",t=>{if(t.defaultPrevented)return;const n=HTMLFormElement.prototype.cloneNode.call(t.target),r=t.submitter;if((r?.formTarget||n.target)==="_blank"||(r?.formMethod||n.method)!=="get")return;const s=new URL(r?.hasAttribute("formaction")&&r?.formAction||n.action);if(he(s,L,!1))return;const o=t.target,l=re(o);if(l.reload)return;t.preventDefault(),t.stopPropagation();const c=new FormData(o,r);s.search=new URLSearchParams(c).toString(),K({type:"form",url:s,keepfocus:l.keepfocus,noscroll:l.noscroll,replace_state:l.replace_state??s.href===location.href,event:t})}),addEventListener("popstate",async t=>{if(!Re){if(t.state?.[B]){const n=t.state[B];if(N={},n===v)return;const r=C[n],a=t.state[Ze]??{},i=new URL(t.state[jt]??location.href),s=t.state[H],o=m.url?we(location)===we(m.url):!1;if(s===x&&(it||o)){a!==E.state&&(E.state=a),e(i),C[v]=q(),r&&scrollTo(r.x,r.y),v=n;return}const c=n-v;await K({type:"popstate",url:i,popped:{state:a,scroll:r,delta:c},accept:()=>{v=n,x=s},block:()=>{history.go(-c)},nav_token:N,event:t})}else if(!F){const n=new URL(location.href);e(n),y.hash&&location.reload()}}}),addEventListener("hashchange",()=>{F&&(F=!1,history.replaceState({...history.state,[B]:++v,[H]:x},"",location.href))});for(const t of document.querySelectorAll("link"))Mt.has(t.rel)&&(t.href=t.href);addEventListener("pageshow",t=>{t.persisted&&$.navigating.set(J.current=null)});function e(t){m.url=E.url=t,$.page.set(Ve(E)),$.page.notify()}}async function an(e,{status:t=200,error:n,node_ids:r,params:a,route:i,server_route:s,data:o,form:l}){st=!0;const c=new URL(location.href);let f;({params:a={},route:i={id:null}}=await pe(c,!1)||{}),f=$e.find(({id:h})=>h===i.id);let p,w=!0;try{const h=r.map(async(_,g)=>{const d=o[g];return d?.uses&&(d.uses=on(d.uses)),je({loader:y.nodes[_],url:c,params:a,route:i,parent:async()=>{const b={};for(let S=0;S{const o=history.state;Re=!0,location.replace(new URL(`#${r}`,location.href)),history.replaceState(o,"",e),t&&scrollTo(i,s),Re=!1})}else{const i=document.body,s=i.getAttribute("tabindex");i.tabIndex=-1,i.focus({preventScroll:!0,focusVisible:!1}),s!==null?i.setAttribute("tabindex",s):i.removeAttribute("tabindex")}const a=getSelection();if(a&&a.type!=="None"){const i=[];for(let s=0;s{if(a.rangeCount===i.length){for(let s=0;s{i=c,s=f});return o.catch(()=>{}),{navigation:{from:{params:e.params,route:{id:e.route?.id??null},url:e.url,scroll:q()},to:n&&{params:t?.params??null,route:{id:t?.route?.id??null},url:n,scroll:a},willUnload:!t,type:r,complete:o},fulfil:i,reject:s}}function Ve(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function ln(e){const t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function _t(e){let t;if(y.hash){const[,,n]=e.hash.split("#",3);t=n??""}else t=e.hash.slice(1);return decodeURIComponent(t)}export{gn as a,_n as g,fn as l,E as p,$ as s}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/D7Bf6Say.js - -```js -var r=`var WebWorker=function(r){"use strict";var n=Uint8Array,e=Uint16Array,f=Uint32Array,t=new n([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),a=new n([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),o=new n([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),i=function(r,n){for(var t=new e(31),a=0;a<31;++a)t[a]=n+=1<>>1|(21845&h)<<1;g=(61680&(g=(52428&g)>>>2|(13107&g)<<2))>>>4|(3855&g)<<4,c[h]=((65280&g)>>>8|(255&g)<<8)>>>1}var w=function(r,n,f){for(var t=r.length,a=0,o=new e(n);a>>u]=s}else for(i=new e(t),a=0;a>>15-r[a];return i},d=new n(288);for(h=0;h<144;++h)d[h]=8;for(h=144;h<256;++h)d[h]=9;for(h=256;h<280;++h)d[h]=7;for(h=280;h<288;++h)d[h]=8;var m=new n(32);for(h=0;h<32;++h)m[h]=5;var y=w(d,9,0),M=w(m,5,0),p=function(r){return(r/8|0)+(7&r&&1)},b=function(r,t,a){(null==a||a>r.length)&&(a=r.length);var o=new(r instanceof e?e:r instanceof f?f:n)(a-t);return o.set(r.subarray(t,a)),o},C=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8},S=function(r,n,e){e<<=7&n;var f=n/8|0;r[f]|=e,r[f+1]|=e>>>8,r[f+2]|=e>>>16},x=function(r,f){for(var t=[],a=0;ag&&(g=i[a].s);var w=new e(g+1),d=A(t[c-1],w,0);if(d>f){a=0;var m=0,y=d-f,M=1<f))break;m+=M-(1<>>=y;m>0;){var b=i[a].s;w[b]=0&&m;--a){var C=i[a].s;w[C]==f&&(--w[C],++m)}d=f}return[new n(w),d]},A=function(r,n,e){return-1==r.s?Math.max(A(r.l,n,e+1),A(r.r,n,e+1)):n[r.s]=e},O=function(r){for(var n=r.length;n&&!r[--n];);for(var f=new e(++n),t=0,a=r[0],o=1,i=function(r){f[t++]=r},v=1;v<=n;++v)if(r[v]==a&&v!=n)++o;else{if(!a&&o>2){for(;o>138;o-=138)i(32754);o>2&&(i(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(i(a),--o;o>6;o-=6)i(8304);o>2&&(i(o-3<<5|8208),o=0)}for(;o--;)i(a);o=1,a=r[v]}return[f.subarray(0,t),n]},T=function(r,n){for(var e=0,f=0;f>>8,r[t+2]=255^r[t],r[t+3]=255^r[t+1];for(var a=0;a4&&!B[o[G-1]];--G);var H,K,L,Q,R=h+5<<3,V=T(v,d)+T(u,m)+s,X=T(v,b)+T(u,J)+s+14+3*G+T(P,B)+(2*P[16]+3*P[17]+7*P[18]);if(R<=V&&R<=X)return k(n,g,r.subarray(c,c+h));if(C(n,g,1+(X15&&(C(n,g,rr[q]>>>5&127),g+=rr[q]>>>12)}}}else H=y,K=d,L=M,Q=m;for(q=0;q255){nr=i[q]>>>18&31;S(n,g,H[nr+257]),g+=K[nr+257],nr>7&&(C(n,g,i[q]>>>23&31),g+=t[nr]);var er=31&i[q];S(n,g,L[er]),g+=Q[er],er>3&&(S(n,g,i[q]>>>5&8191),g+=a[er])}else S(n,g,H[i[q]]),g+=K[i[q]];return S(n,g,H[256]),g+K[256]},J=new f([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),N=function(r,o,i,v,u){return function(r,o,i,v,u,c){var h=r.length,g=new n(v+h+5*(1+Math.floor(h/7e3))+u),w=g.subarray(v,g.length-u),d=0;if(!o||h<8)for(var m=0;m<=h;m+=65535){var y=m+65535;y>>13,S=8191&M,x=(1<7e3||P>24576)&&H>423){d=E(r,w,0,D,I,W,j,P,z,m-z,d),P=_=j=0,z=m;for(var K=0;K<286;++K)I[K]=0;for(K=0;K<30;++K)W[K]=0}var L=2,Q=0,R=S,V=F-G&32767;if(H>2&&B==U(m-V))for(var X=Math.min(C,H)-1,Y=Math.min(32767,m),Z=Math.min(258,H);V<=Y&&--R&&F!=G;){if(r[m+L]==r[m+L-V]){for(var $=0;$L){if(L=$,Q=V,$>X)break;var rr=Math.min(V,$-2),nr=0;for(K=0;Knr&&(nr=fr,G=er)}}}V+=(F=G)-(G=A[F])+32768&32767}if(Q){D[P++]=268435456|s[L]<<18|l[Q];var tr=31&s[L],ar=31&l[Q];j+=t[tr]+a[ar],++I[257+tr],++W[ar],q=m+L,++_}else D[P++]=r[m],++I[r[m]]}}d=E(r,w,c,D,I,W,j,P,z,m-z,d)}return b(g,0,v+p(d)+u)}(r,null==o.level?6:o.level,null==o.mem?Math.ceil(1.5*Math.max(8,Math.min(13,Math.log(r.length)))):12+o.mem,i,v,!0)};function U(r,n){void 0===n&&(n={});var e=function(){var r=1,n=0;return{p:function(e){for(var f=r,t=n,a=e.length,o=0;o!=a;){for(var i=Math.min(o+5552,a);o>>8<<16|(255&n)<<8|n>>>8)+2*((255&r)<<23)}}}();e.p(r);var f,t,a,o=N(r,n,2,4);return f=o,t=n.level,a=0==t?0:t<6?1:9==t?3:2,f[0]=120,f[1]=a<<6|(a?32-2*a:1),function(r,n,e){for(;e;++n)r[n]=e,e>>>=8}(o,o.length-4,e.d()),o}const D=r=>{const e={...r,v:"v1"};return function(r,n){var e="";if(!n&&"undefined"!=typeof TextDecoder)return(new TextDecoder).decode(r);for(var f=0;f>10,56320|1023&t))}return e}(U(function(r,e){var f=r.length;if(!e&&"undefined"!=typeof TextEncoder)return(new TextEncoder).encode(r);for(var t=new n(r.length+(r.length>>>1)),a=0,o=function(r){t[a++]=r},i=0;it.length){var v=new n(a+8+(f-i<<1));v.set(t),t=v}var u=r.charCodeAt(i);u<128||e?o(u):u<2048?(o(192|u>>>6),o(128|63&u)):u>55295&&u<57344?(o(240|(u=65536+(1047552&u)|1023&r.charCodeAt(++i))>>>18),o(128|u>>>12&63),o(128|u>>>6&63),o(128|63&u)):(o(224|u>>>12),o(128|u>>>6&63),o(128|63&u))}return b(t,0,a)}(JSON.stringify(e))),!0)};onmessage=r=>{const{event:n,sessionId:e}="string"==typeof r.data?JSON.parse(r.data):r.data,f=JSON.stringify(D(n));postMessage({compressedEvent:f,sessionId:e})};const I=onmessage;return r.compressionOnMessage=I,Object.defineProperty(r,"__esModule",{value:!0}),r}({}); -`;export{r as compressionScript}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/DMrp4Fbu.js - -```js -var rt={},on=Object.defineProperty,an=(e,t,r)=>t in e?on(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,I=(e,t,r)=>an(e,typeof t!="symbol"?t+"":t,r),Jr,ln=Object.defineProperty,un=(e,t,r)=>t in e?ln(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,Yr=(e,t,r)=>un(e,typeof t!="symbol"?t+"":t,r),Y=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Y||{});const Qr={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},Xr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},je={};function cn(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function kr(e){if(je[e])return je[e];const t=cn(e)||globalThis[e],r=t.prototype,l=e in Qr?Qr[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in Xr?Xr[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return je[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?je[e]=p:r}catch{return r}}const Ct={};function be(e,t,r){var l;const s=`${e}.${String(r)}`;if(Ct[s])return Ct[s].call(t);const f=kr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Ct[s]=u,u.call(t)):t[r]}const xt={};function wi(e,t,r){const l=`${e}.${String(r)}`;if(xt[l])return xt[l].bind(t);const f=kr(e)[r];return typeof f!="function"?t[r]:(xt[l]=f,f.bind(t))}function hn(e){return be("Node",e,"childNodes")}function fn(e){return be("Node",e,"parentNode")}function pn(e){return be("Node",e,"parentElement")}function dn(e){return be("Node",e,"textContent")}function mn(e,t){return wi("Node",e,"contains")(t)}function gn(e){return wi("Node",e,"getRootNode")()}function yn(e){return!e||!("host"in e)?null:be("ShadowRoot",e,"host")}function wn(e){return e.styleSheets}function bn(e){return!e||!("shadowRoot"in e)?null:be("Element",e,"shadowRoot")}function Sn(e,t){return be("Element",e,"querySelector")(t)}function vn(e,t){return be("Element",e,"querySelectorAll")(t)}function Cn(){return kr("MutationObserver").constructor}const K={childNodes:hn,parentNode:fn,parentElement:pn,textContent:dn,contains:mn,getRootNode:gn,host:yn,styleSheets:wn,shadowRoot:bn,querySelector:Sn,querySelectorAll:vn,mutationObserver:Cn};function bi(e){return e.nodeType===e.ELEMENT_NODE}function Te(e){const t=e&&"host"in e&&"mode"in e&&K.host(e)||null;return!!(t&&"shadowRoot"in t&&K.shadowRoot(t)===e)}function Fe(e){return Object.prototype.toString.call(e)==="[object ShadowRoot]"}function xn(e){return e.includes(" background-clip: text;")&&!e.includes(" -webkit-background-clip: text;")&&(e=e.replace(/\sbackground-clip:\s*text;/g," -webkit-background-clip: text; background-clip: text;")),e}function Rn(e){const{cssText:t}=e;if(t.split('"').length<3)return t;const r=["@import",`url(${JSON.stringify(e.href)})`];return e.layerName===""?r.push("layer"):e.layerName&&r.push(`layer(${e.layerName})`),e.supportsText&&r.push(`supports(${e.supportsText})`),e.media.length&&r.push(e.media.mediaText),r.join(" ")+";"}function Mr(e){try{const t=e.rules||e.cssRules;if(!t)return null;let r=e.href;!r&&e.ownerNode&&e.ownerNode.ownerDocument&&(r=e.ownerNode.ownerDocument.location.href);const l=Array.from(t,s=>Si(s,r)).join("");return xn(l)}catch{return null}}function Si(e,t){if(Mn(e)){let r;try{r=Mr(e.styleSheet)||Rn(e)}catch{r=e.cssText}return e.styleSheet.href?nt(r,e.styleSheet.href):r}else{let r=e.cssText;return En(e)&&e.selectorText.includes(":")&&(r=On(r)),t?nt(r,t):r}}function On(e){const t=/(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;return e.replace(t,"$1\\$2")}function Mn(e){return"styleSheet"in e}function En(e){return"selectorText"in e}class vi{constructor(){Yr(this,"idNodeMap",new Map),Yr(this,"nodeMetaMap",new WeakMap)}getId(t){var r;return t?((r=this.getMeta(t))==null?void 0:r.id)??-1:-1}getNode(t){return this.idNodeMap.get(t)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(t){return this.nodeMetaMap.get(t)||null}removeNodeFromMap(t){const r=this.getId(t);this.idNodeMap.delete(r),t.childNodes&&t.childNodes.forEach(l=>this.removeNodeFromMap(l))}has(t){return this.idNodeMap.has(t)}hasNode(t){return this.nodeMetaMap.has(t)}add(t,r){const l=r.id;this.idNodeMap.set(l,t),this.nodeMetaMap.set(t,r)}replace(t,r){const l=this.getNode(t);if(l){const s=this.nodeMetaMap.get(l);s&&this.nodeMetaMap.set(r,s)}this.idNodeMap.set(t,r)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function In(){return new vi}function st({element:e,maskInputOptions:t,tagName:r,type:l,value:s,maskInputFn:f}){let u=s||"";const m=l&&Ce(l);return(t[r.toLowerCase()]||m&&t[m])&&(f?u=f(u,e):u="*".repeat(u.length)),u}function Ce(e){return e.toLowerCase()}const Kr="__rrweb_original__";function An(e){const t=e.getContext("2d");if(!t)return!0;const r=50;for(let l=0;la!==0))return!1}return!0}function it(e){const t=e.type;return e.hasAttribute("data-rr-is-password")?"password":t?Ce(t):null}function Ci(e,t){let r;try{r=new URL(e,t??window.location.href)}catch{return null}const l=/\.([0-9a-z]+)(?:$)/i,s=r.pathname.match(l);return s?.[1]??null}function kn(e){let t="";return e.indexOf("//")>-1?t=e.split("/").slice(0,3).join("/"):t=e.split("/")[0],t=t.split("?")[0],t}const Nn=/url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm,Pn=/^(?:[a-z+]+:)?\/\//i,_n=/^www\..*/i,Ln=/^(data:)([^,]*),(.*)/i;function nt(e,t){return(e||"").replace(Nn,(r,l,s,f,u,m)=>{const a=s||u||m,p=l||f||"";if(!a)return r;if(Pn.test(a)||_n.test(a))return`url(${p}${a}${p})`;if(Ln.test(a))return`url(${p}${a}${p})`;if(a[0]==="/")return`url(${p}${kn(t)+a}${p})`;const i=t.split("/"),c=a.split("/");i.pop();for(const o of c)o!=="."&&(o===".."?i.pop():i.push(o));return`url(${p}${i.join("/")}${p})`})}function He(e,t=!1){return t?e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,""):e.replace(/(\/\*[^*]*\*\/)|[\s;]/g,"").replace(/0px/g,"0")}function Dn(e,t,r=!1){const l=Array.from(t.childNodes),s=[];let f=0;if(l.length>1&&e&&typeof e=="string"){let u=He(e,r);const m=u.length/e.length;for(let a=1;a2&&n[0]===""&&l[a-1].textContent!=="")d=u.indexOf(o,1);else if(n.length===1){if(o=o.substring(0,o.length-1),n=u.split(o),n.length<=1)return s.push(e),s;c=i+1}else c===p.length-1&&(d=u.indexOf(o));if(n.length>=2&&c>i){const h=l[a-1].textContent;if(h&&typeof h=="string"){const y=He(h).length;d=u.indexOf(o,y)}d===-1&&(d=n[0].length)}if(d!==-1){let h=Math.floor(d/m);for(;h>0&&h50*l.length)return s.push(e),s;const y=He(e.substring(0,h),r);if(y.length===d){s.push(e.substring(0,h)),e=e.substring(h),u=u.substring(d);break}else y.length=t.length);){let f=l(zn);if(f.slice(-1)===",")f=ke(e,f.substring(0,f.length-1)),s.push(f);else{let u="";f=ke(e,f);let m=!1;for(;;){const a=t.charAt(r);if(a===""){s.push((f+u).trim());break}else if(m)a===")"&&(m=!1);else if(a===","){r+=1,s.push((f+u).trim());break}else a==="("&&(m=!0);u+=a,r+=1}}}return s.join(", ")}const es=new WeakMap;function ke(e,t){return!t||t.trim()===""?t:Nr(e,t)}function jn(e){return!!(e.tagName==="svg"||e.ownerSVGElement)}function Nr(e,t){let r=es.get(e);if(r||(r=e.createElement("a"),es.set(e,r)),!t)t="";else if(t.startsWith("blob:")||t.startsWith("data:"))return t;return r.setAttribute("href",t),r.href}function Ri(e,t,r,l){return l&&(r==="src"||r==="href"&&!(t==="use"&&l[0]==="#")||r==="xlink:href"&&l[0]!=="#"||r==="background"&&(t==="table"||t==="td"||t==="th")?ke(e,l):r==="srcset"?qn(e,l):r==="style"?nt(l,Nr(e)):t==="object"&&r==="data"?ke(e,l):l)}function Oi(e,t,r){return(e==="video"||e==="audio")&&t==="autoplay"}function Hn(e,t,r){try{if(typeof t=="string"){if(e.classList.contains(t))return!0}else for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}if(r)return e.matches(r)}catch{}return!1}function ot(e,t,r){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return r?ot(K.parentNode(e),t,r):!1;for(let l=e.classList.length;l--;){const s=e.classList[l];if(t.test(s))return!0}return r?ot(K.parentNode(e),t,r):!1}function Mi(e,t,r,l){let s;if(bi(e)){if(s=e,!K.childNodes(s).length)return!1}else{if(K.parentElement(e)===null)return!1;s=K.parentElement(e)}try{if(typeof t=="string"){if(l){if(s.closest(`.${t}`))return!0}else if(s.classList.contains(t))return!0}else if(ot(s,t,l))return!0;if(r){if(l){if(s.closest(r))return!0}else if(s.matches(r))return!0}}catch{}return!1}function Gn(e,t,r){const l=e.contentWindow;if(!l)return;let s=!1,f;try{f=l.document.readyState}catch{return}if(f!=="complete"){const m=setTimeout(()=>{s||(t(),s=!0)},r);e.addEventListener("load",()=>{clearTimeout(m),s=!0,t()});return}const u="about:blank";if(l.location.href!==u||e.src===u||e.src==="")return setTimeout(t,0),e.addEventListener("load",t);e.addEventListener("load",t)}function Vn(e,t,r){let l=!1,s;try{s=e.sheet}catch{return}if(s)return;const f=setTimeout(()=>{l||(t(),l=!0)},r);e.addEventListener("load",()=>{clearTimeout(f),l=!0,t()})}function Jn(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:u,inlineStylesheet:m,maskInputOptions:a={},maskTextFn:p,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o={},inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y=!1,cssCaptured:C=!1,applyBackgroundColorToBlockedElements:w=!1}=t,S=Yn(r,l);switch(e.nodeType){case e.DOCUMENT_NODE:return e.compatMode!=="CSS1Compat"?{type:Y.Document,childNodes:[],compatMode:e.compatMode}:{type:Y.Document,childNodes:[]};case e.DOCUMENT_TYPE_NODE:return{type:Y.DocumentType,name:e.name,publicId:e.publicId,systemId:e.systemId,rootId:S};case e.ELEMENT_NODE:return Xn(e,{doc:r,blockClass:s,blockSelector:f,inlineStylesheet:m,maskInputOptions:a,maskInputFn:i,maskAttributeFn:c,dataURLOptions:o,inlineImages:n,recordCanvas:d,keepIframeSrcFn:h,newlyAddedElement:y,rootId:S,applyBackgroundColorToBlockedElements:w});case e.TEXT_NODE:return Qn(e,{doc:r,needsMask:u,maskTextFn:p,rootId:S,cssCaptured:C});case e.CDATA_SECTION_NODE:return{type:Y.CDATA,textContent:"",rootId:S};case e.COMMENT_NODE:return{type:Y.Comment,textContent:K.textContent(e)||"",rootId:S};default:return!1}}function Yn(e,t){if(!t.hasNode(e))return;const r=t.getId(e);return r===1?void 0:r}function Qn(e,t){const{needsMask:r,maskTextFn:l,rootId:s,cssCaptured:f}=t,u=K.parentNode(e),m=u&&u.tagName;let a="";const p=m==="STYLE"?!0:void 0,i=m==="SCRIPT"?!0:void 0;return i?a="SCRIPT_PLACEHOLDER":f||(a=K.textContent(e),p&&a&&(a=nt(a,Nr(t.doc)))),!p&&!i&&a&&r&&(a=l?l(a,K.parentElement(e)):a.replace(/[\S]/g,"*")),{type:Y.Text,textContent:a||"",rootId:s}}function Xn(e,t){const{doc:r,blockClass:l,blockSelector:s,inlineStylesheet:f,maskInputOptions:u={},maskInputFn:m,maskAttributeFn:a,dataURLOptions:p={},inlineImages:i,recordCanvas:c,keepIframeSrcFn:o,newlyAddedElement:n=!1,rootId:d,applyBackgroundColorToBlockedElements:h=!1}=t,y=Hn(e,l,s),C=Bn(e);let w={};const S=e.attributes.length;for(let g=0;gx.href===e.href);let v=null;g&&(v=Mr(g)),v&&(delete w.rel,delete w.href,w._cssText=v)}if(C==="style"&&e.sheet){let g=Mr(e.sheet);g&&(e.childNodes.length>1&&(g=Tn(g,e)),w._cssText=g)}if(C==="input"||C==="textarea"||C==="select"){const g=e.value,v=e.checked;w.type!=="radio"&&w.type!=="checkbox"&&w.type!=="submit"&&w.type!=="button"&&g?w.value=st({element:e,type:it(e),tagName:C,value:g,maskInputOptions:u,maskInputFn:m}):v&&(w.checked=v)}if(C==="option"&&(e.selected&&!u.select?w.selected=!0:delete w.selected),C==="dialog"&&e.open&&(w.rr_open_mode=e.matches("dialog:modal")?"modal":"non-modal"),C==="canvas"&&c){if(e.__context==="2d")An(e)||(w.rr_dataURL=e.toDataURL(p.type,p.quality));else if(!("__context"in e)){const g=e.toDataURL(p.type,p.quality),v=r.createElement("canvas");v.width=e.width,v.height=e.height;const x=v.toDataURL(p.type,p.quality);g!==x&&(w.rr_dataURL=g)}}if(C==="img"&&i){Ee||(Ee=r.createElement("canvas"),Zr=Ee.getContext("2d"));const g=e,v=g.currentSrc||g.getAttribute("src")||"",x=g.crossOrigin,O=()=>{g.removeEventListener("load",O);try{Ee.width=g.naturalWidth,Ee.height=g.naturalHeight,Zr.drawImage(g,0,0),w.rr_dataURL=Ee.toDataURL(p.type,p.quality)}catch(A){if(g.crossOrigin!=="anonymous"){g.crossOrigin="anonymous",g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O);return}else console.warn(`Cannot inline img src=${v}! Error: ${A}`)}g.crossOrigin==="anonymous"&&(x?w.crossOrigin=x:g.removeAttribute("crossorigin"))};g.complete&&g.naturalWidth!==0?O():g.addEventListener("load",O)}if(C==="audio"||C==="video"){const g=w;g.rr_mediaState=e.paused?"paused":"played",g.rr_mediaCurrentTime=e.currentTime,g.rr_mediaPlaybackRate=e.playbackRate,g.rr_mediaMuted=e.muted,g.rr_mediaLoop=e.loop,g.rr_mediaVolume=e.volume}if(n||(e.scrollLeft&&(w.rr_scrollLeft=e.scrollLeft),e.scrollTop&&(w.rr_scrollTop=e.scrollTop)),y){const{width:g,height:v}=e.getBoundingClientRect();w={class:w.class,rr_width:`${g}px`,rr_height:`${v}px`,...h?{rr_background_color:Fn}:{}}}C==="iframe"&&!o(w.src)&&(e.contentDocument||(w.rr_src=w.src),delete w.src);let b;try{customElements.get(C)&&(b=!0)}catch{}return{type:Y.Element,tagName:C,attributes:w,childNodes:[],isSVG:jn(e)||void 0,needBlock:y,rootId:d,isCustom:b}}function z(e){return e==null?"":e.toLowerCase()}function Kn(e,t){if(t.comment&&e.type===Y.Comment)return!0;if(e.type===Y.Element){if(t.script&&(e.tagName==="script"||e.tagName==="link"&&(e.attributes.rel==="preload"||e.attributes.rel==="modulepreload")&&e.attributes.as==="script"||e.tagName==="link"&&e.attributes.rel==="prefetch"&&typeof e.attributes.href=="string"&&Ci(e.attributes.href)==="js"))return!0;if(t.headFavicon&&(e.tagName==="link"&&e.attributes.rel==="shortcut icon"||e.tagName==="meta"&&(z(e.attributes.name).match(/^msapplication-tile(image|color)$/)||z(e.attributes.name)==="application-name"||z(e.attributes.rel)==="icon"||z(e.attributes.rel)==="apple-touch-icon"||z(e.attributes.rel)==="shortcut icon")))return!0;if(e.tagName==="meta"){if(t.headMetaDescKeywords&&z(e.attributes.name).match(/^description|keywords$/))return!0;if(t.headMetaSocial&&(z(e.attributes.property).match(/^(og|twitter|fb):/)||z(e.attributes.name).match(/^(og|twitter):/)||z(e.attributes.name)==="pinterest"))return!0;if(t.headMetaRobots&&(z(e.attributes.name)==="robots"||z(e.attributes.name)==="googlebot"||z(e.attributes.name)==="bingbot"))return!0;if(t.headMetaHttpEquiv&&e.attributes["http-equiv"]!==void 0)return!0;if(t.headMetaAuthorship&&(z(e.attributes.name)==="author"||z(e.attributes.name)==="generator"||z(e.attributes.name)==="framework"||z(e.attributes.name)==="publisher"||z(e.attributes.name)==="progid"||z(e.attributes.property).match(/^article:/)||z(e.attributes.property).match(/^product:/)))return!0;if(t.headMetaVerification&&(z(e.attributes.name)==="google-site-verification"||z(e.attributes.name)==="yandex-verification"||z(e.attributes.name)==="csrf-token"||z(e.attributes.name)==="p:domain_verify"||z(e.attributes.name)==="verify-v1"||z(e.attributes.name)==="verification"||z(e.attributes.name)==="shopify-checkout-api-token"))return!0}}return!1}function Ne(e,t){const{doc:r,mirror:l,blockClass:s,blockSelector:f,maskTextClass:u,maskTextSelector:m,skipChild:a=!1,inlineStylesheet:p=!0,maskInputOptions:i={},maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h={},inlineImages:y=!1,recordCanvas:C=!1,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b=5e3,onStylesheetLoad:g,stylesheetLoadTimeout:v=5e3,keepIframeSrcFn:x=()=>!1,newlyAddedElement:O=!1,cssCaptured:A=!1,applyBackgroundColorToBlockedElements:M=!1}=t;let{needsMask:$}=t,{preserveWhiteSpace:k=!0}=t;$||($=Mi(e,u,m,$===void 0));const R=Jn(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,dataURLOptions:h,inlineImages:y,recordCanvas:C,keepIframeSrcFn:x,newlyAddedElement:O,cssCaptured:A,applyBackgroundColorToBlockedElements:M});if(!R)return console.warn(e,"not serialized"),null;let Z;l.hasNode(e)?Z=l.getId(e):Kn(R,d)||!k&&R.type===Y.Text&&!R.textContent.replace(/^\s+|\s+$/gm,"").length?Z=$e:Z=xi();const N=Object.assign(R,{id:Z});if(l.add(e,N),Z===$e)return null;w&&w(e);let ee=!a;if(N.type===Y.Element){ee=ee&&!N.needBlock,delete N.needBlock;const F=K.shadowRoot(e);F&&Fe(F)&&(N.isShadowHost=!0)}if((N.type===Y.Document||N.type===Y.Element)&&ee){d.headWhitespace&&N.type===Y.Element&&N.tagName==="head"&&(k=!1);const F={doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:a,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x,cssCaptured:!1,applyBackgroundColorToBlockedElements:M};if(!(N.type===Y.Element&&N.tagName==="textarea"&&N.attributes.value!==void 0)){N.type===Y.Element&&N.attributes._cssText!==void 0&&typeof N.attributes._cssText=="string"&&(F.cssCaptured=!0);for(const W of Array.from(K.childNodes(e))){const P=Ne(W,F);P&&N.childNodes.push(P)}}let Q=null;if(bi(e)&&(Q=K.shadowRoot(e)))for(const W of Array.from(K.childNodes(Q))){const P=Ne(W,F);P&&(Fe(Q)&&(P.isShadow=!0),N.childNodes.push(P))}}const H=K.parentNode(e);return H&&Te(H)&&Fe(H)&&(N.isShadow=!0),N.type===Y.Element&&N.tagName==="iframe"&&Gn(e,()=>{const F=e.contentDocument;if(F&&S){const Q=Ne(F,{doc:F,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});Q&&S(e,Q)}},b),N.type===Y.Element&&N.tagName==="link"&&typeof N.attributes.rel=="string"&&(N.attributes.rel==="stylesheet"||N.attributes.rel==="preload"&&typeof N.attributes.href=="string"&&Ci(N.attributes.href)==="css")&&Vn(e,()=>{if(g){const F=Ne(e,{doc:r,mirror:l,blockClass:s,blockSelector:f,needsMask:$,maskTextClass:u,maskTextSelector:m,skipChild:!1,inlineStylesheet:p,maskInputOptions:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d,dataURLOptions:h,inlineImages:y,recordCanvas:C,preserveWhiteSpace:k,onSerialize:w,onIframeLoad:S,iframeLoadTimeout:b,onStylesheetLoad:g,stylesheetLoadTimeout:v,keepIframeSrcFn:x});F&&g(e,F)}},v),N}function Zn(e,t){const{mirror:r=new vi,blockClass:l="rr-block",blockSelector:s=null,maskTextClass:f="rr-mask",maskTextSelector:u=null,inlineStylesheet:m=!0,inlineImages:a=!1,recordCanvas:p=!1,maskAllInputs:i=!1,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOM:d=!1,dataURLOptions:h,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v=()=>!1,applyBackgroundColorToBlockedElements:x=!1}=t||{};return Ne(e,{doc:e,mirror:r,blockClass:l,blockSelector:s,maskTextClass:f,maskTextSelector:u,skipChild:!1,inlineStylesheet:m,maskInputOptions:i===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:i===!1?{password:!0}:i,maskTextFn:c,maskInputFn:o,maskAttributeFn:n,slimDOMOptions:d===!0||d==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:d==="all",headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0,headMetaVerification:!0}:d===!1?{}:d,dataURLOptions:h,inlineImages:a,recordCanvas:p,preserveWhiteSpace:y,onSerialize:C,onIframeLoad:w,iframeLoadTimeout:S,onStylesheetLoad:b,stylesheetLoadTimeout:g,keepIframeSrcFn:v,newlyAddedElement:!1,applyBackgroundColorToBlockedElements:x})}function eo(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function to(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Ge={exports:{}},ts;function ro(){if(ts)return Ge.exports;ts=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Ge.exports=t(),Ge.exports.createColors=t,Ge.exports}const so={},io=Object.freeze(Object.defineProperty({__proto__:null,default:so},Symbol.toStringTag,{value:"Module"})),he=to(io);var Rt,rs;function Pr(){if(rs)return Rt;rs=1;let e=ro(),t=he;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Rt=r,r.default=r,Rt}var Ve={},ss;function _r(){return ss||(ss=1,Ve.isClean=Symbol("isClean"),Ve.my=Symbol("my")),Ve}var Ot,is;function Ei(){if(is)return Ot;is=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Ot=r,r.default=r,Ot}var Mt,ns;function ct(){if(ns)return Mt;ns=1;let e=Ei();function t(r,l){new e(l).stringify(r)}return Mt=t,t.default=t,Mt}var Et,os;function ht(){if(os)return Et;os=1;let{isClean:e,my:t}=_r(),r=Pr(),l=Ei(),s=ct();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Et=u,u.default=u,Et}var It,as;function ft(){if(as)return It;as=1;let e=ht();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return It=t,t.default=t,It}var At,ls;function no(){if(ls)return At;ls=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return At={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},At}var kt,us;function Ii(){if(us)return kt;us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{existsSync:r,readFileSync:l}=he,{dirname:s,join:f}=he;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return kt=m,m.default=m,kt}var Nt,cs;function pt(){if(cs)return Nt;cs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{fileURLToPath:r,pathToFileURL:l}=he,{isAbsolute:s,resolve:f}=he,{nanoid:u}=no(),m=he,a=Pr(),p=Ii(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return Nt=n,n.default=n,m&&m.registerInput&&m.registerInput(n),Nt}var Pt,hs;function Ai(){if(hs)return Pt;hs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=he,{dirname:r,relative:l,resolve:s,sep:f}=he,{pathToFileURL:u}=he,m=pt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return Pt=i,Pt}var _t,fs;function dt(){if(fs)return _t;fs=1;let e=ht();class t extends e{constructor(l){super(l),this.type="comment"}}return _t=t,t.default=t,_t}var Lt,ps;function xe(){if(ps)return Lt;ps=1;let{isClean:e,my:t}=_r(),r=ft(),l=dt(),s=ht(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},Lt=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},Lt}var Dt,ds;function Lr(){if(ds)return Dt;ds=1;let e=xe(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Dt=l,l.default=l,Dt}var Tt,ms;function ki(){if(ms)return Tt;ms=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Tt=e,e.default=e,Tt}var Ft,gs;function Dr(){if(gs)return Ft;gs=1;let e=ki();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return Ft=t,t.default=t,Ft}var Ut,ys;function oo(){if(ys)return Ut;ys=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return Ut=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},Ut}var $t,ws;function Tr(){if(ws)return $t;ws=1;let e=xe();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return $t=t,t.default=t,e.registerAtRule(t),$t}var Bt,bs;function ze(){if(bs)return Bt;bs=1;let e=xe(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},Bt=l,l.default=l,e.registerRoot(l),Bt}var zt,Ss;function Ni(){if(Ss)return zt;Ss=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return zt=e,e.default=e,zt}var Wt,vs;function Fr(){if(vs)return Wt;vs=1;let e=xe(),t=Ni();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return Wt=r,r.default=r,e.registerRule(r),Wt}var qt,Cs;function ao(){if(Cs)return qt;Cs=1;let e=ft(),t=oo(),r=dt(),l=Tr(),s=ze(),f=Fr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},Ht=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),Ht}var Gt,Os;function lo(){if(Os)return Gt;Os=1;let e=Ai(),t=ct(),r=Ur();const l=Dr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return Gt=s,s.default=s,Gt}var Vt,Ms;function uo(){if(Ms)return Vt;Ms=1;let e=lo(),t=Pi(),r=Lr(),l=ze();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return Vt=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),Vt}var Jt,Es;function co(){if(Es)return Jt;Es=1;let e=ft(),t=Ii(),r=dt(),l=Tr(),s=pt(),f=ze(),u=Fr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Jt=m,m.default=m,Jt}var Yt,Is;function ho(){if(Is)return Yt;Is=1;let e=Pr(),t=ft(),r=Pi(),l=xe(),s=uo(),f=ct(),u=co(),m=Lr(),a=ki(),p=dt(),i=Tr(),c=Dr(),o=pt(),n=Ur(),d=Ni(),h=Fr(),y=ze(),C=ht();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),Yt=w,w.default=w,Yt}var fo=ho();const q=eo(fo);q.stringify;q.fromJSON;q.plugin;q.parse;q.list;q.document;q.comment;q.atRule;q.rule;q.decl;q.root;q.CssSyntaxError;q.Declaration;q.Container;q.Processor;q.Document;q.Comment;q.Warning;q.AtRule;q.Result;q.Input;q.Rule;q.Root;q.Node;var po=Object.defineProperty,mo=(e,t,r)=>t in e?po(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,oe=(e,t,r)=>mo(e,typeof t!="symbol"?t+"":t,r);function go(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function yo(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if(typeof t=="function"){var r=function l(){return this instanceof l?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};r.prototype=t.prototype}else r={};return Object.defineProperty(r,"__esModule",{value:!0}),Object.keys(e).forEach(function(l){var s=Object.getOwnPropertyDescriptor(e,l);Object.defineProperty(r,l,s.get?s:{enumerable:!0,get:function(){return e[l]}})}),r}var Je={exports:{}},As;function wo(){if(As)return Je.exports;As=1;var e=String,t=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return Je.exports=t(),Je.exports.createColors=t,Je.exports}const bo={},So=Object.freeze(Object.defineProperty({__proto__:null,default:bo},Symbol.toStringTag,{value:"Module"})),fe=yo(So);var Qt,ks;function $r(){if(ks)return Qt;ks=1;let e=wo(),t=fe;class r extends Error{constructor(s,f,u,m,a,p){super(s),this.name="CssSyntaxError",this.reason=s,a&&(this.file=a),m&&(this.source=m),p&&(this.plugin=p),typeof f<"u"&&typeof u<"u"&&(typeof f=="number"?(this.line=f,this.column=u):(this.line=f.line,this.column=f.column,this.endLine=u.line,this.endColumn=u.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,r)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),t&&s&&(f=t(f));let u=f.split(/\r?\n/),m=Math.max(this.line-3,0),a=Math.min(this.line+2,u.length),p=String(a).length,i,c;if(s){let{bold:o,gray:n,red:d}=e.createColors(!0);i=h=>o(d(h)),c=h=>n(h)}else i=c=o=>o;return u.slice(m,a).map((o,n)=>{let d=m+1+n,h=" "+(" "+d).slice(-p)+" | ";if(d===this.line){let y=c(h.replace(/\d/g," "))+o.slice(0,this.column-1).replace(/[^\t]/g," ");return i(">")+c(h)+o+` - `+y+i("^")}return" "+c(h)+o}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return Qt=r,r.default=r,Qt}var Ye={},Ns;function Br(){return Ns||(Ns=1,Ye.isClean=Symbol("isClean"),Ye.my=Symbol("my")),Ye}var Xt,Ps;function _i(){if(Ps)return Xt;Ps=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function t(l){return l[0].toUpperCase()+l.slice(1)}class r{constructor(s){this.builder=s}atrule(s,f){let u="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?u+=s.raws.afterName:m&&(u+=" "),s.nodes)this.block(s,u+m);else{let a=(s.raws.between||"")+(f?";":"");this.builder(u+m+a,s)}}beforeAfter(s,f){let u;s.type==="decl"?u=this.raw(s,null,"beforeDecl"):s.type==="comment"?u=this.raw(s,null,"beforeComment"):f==="before"?u=this.raw(s,null,"beforeRule"):u=this.raw(s,null,"beforeClose");let m=s.parent,a=0;for(;m&&m.type!=="root";)a+=1,m=m.parent;if(u.includes(` -`)){let p=this.raw(s,null,"indent");if(p.length)for(let i=0;i0&&s.nodes[f].type==="comment";)f-=1;let u=this.raw(s,"semicolon");for(let m=0;m{if(m=c.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[u]),p.rawCache[u]=m,m}rawBeforeClose(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length>0&&typeof u.raws.after<"u")return f=u.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let u;return s.walkComments(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeDecl"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeDecl(s,f){let u;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return u=m.raws.before,u.includes(` -`)&&(u=u.replace(/[^\n]+$/,"")),!1}),typeof u>"u"?u=this.raw(f,null,"beforeRule"):u&&(u=u.replace(/\S/g,"")),u}rawBeforeOpen(s){let f;return s.walk(u=>{if(u.type!=="decl"&&(f=u.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(u=>{if(u.nodes&&(u.parent!==s||s.first!==u)&&typeof u.raws.before<"u")return f=u.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(u=>{if(typeof u.raws.between<"u")return f=u.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length===0&&(f=u.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(u=>{let m=u.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof u.raws.before<"u"){let a=u.raws.before.split(` -`);return f=a[a.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(u=>{if(u.nodes&&u.nodes.length&&u.last.type==="decl"&&(f=u.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let u=s[f],m=s.raws[f];return m&&m.value===u?m.raw:u}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Xt=r,r.default=r,Xt}var Kt,_s;function mt(){if(_s)return Kt;_s=1;let e=_i();function t(r,l){new e(l).stringify(r)}return Kt=t,t.default=t,Kt}var Zt,Ls;function gt(){if(Ls)return Zt;Ls=1;let{isClean:e,my:t}=Br(),r=$r(),l=_i(),s=mt();function f(m,a){let p=new m.constructor;for(let i in m){if(!Object.prototype.hasOwnProperty.call(m,i)||i==="proxyCache")continue;let c=m[i],o=typeof c;i==="parent"&&o==="object"?a&&(p[i]=a):i==="source"?p[i]=c:Array.isArray(c)?p[i]=c.map(n=>f(n,p)):(o==="object"&&c!==null&&(c=f(c)),p[i]=c)}return p}class u{constructor(a={}){this.raws={},this[e]=!1,this[t]=!0;for(let p in a)if(p==="nodes"){this.nodes=[];for(let i of a[p])typeof i.clone=="function"?this.append(i.clone()):this.append(i)}else this[p]=a[p]}addToError(a){if(a.postcssNode=this,a.stack&&this.source&&/\n\s{4}at /.test(a.stack)){let p=this.source;a.stack=a.stack.replace(/\n\s{4}at /,`$&${p.input.from}:${p.start.line}:${p.start.column}$&`)}return a}after(a){return this.parent.insertAfter(this,a),this}assign(a={}){for(let p in a)this[p]=a[p];return this}before(a){return this.parent.insertBefore(this,a),this}cleanRaws(a){delete this.raws.before,delete this.raws.after,a||delete this.raws.between}clone(a={}){let p=f(this);for(let i in a)p[i]=a[i];return p}cloneAfter(a={}){let p=this.clone(a);return this.parent.insertAfter(this,p),p}cloneBefore(a={}){let p=this.clone(a);return this.parent.insertBefore(this,p),p}error(a,p={}){if(this.source){let{end:i,start:c}=this.rangeBy(p);return this.source.input.error(a,{column:c.column,line:c.line},{column:i.column,line:i.line},p)}return new r(a)}getProxyProcessor(){return{get(a,p){return p==="proxyOf"?a:p==="root"?()=>a.root().toProxy():a[p]},set(a,p,i){return a[p]===i||(a[p]=i,(p==="prop"||p==="value"||p==="name"||p==="params"||p==="important"||p==="text")&&a.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let a=this;for(;a=a.parent;)a[e]=!1}}next(){if(!this.parent)return;let a=this.parent.index(this);return this.parent.nodes[a+1]}positionBy(a,p){let i=this.source.start;if(a.index)i=this.positionInside(a.index,p);else if(a.word){p=this.toString();let c=p.indexOf(a.word);c!==-1&&(i=this.positionInside(c,p))}return i}positionInside(a,p){let i=p||this.toString(),c=this.source.start.column,o=this.source.start.line;for(let n=0;ntypeof h=="object"&&h.toJSON?h.toJSON(null,p):h);else if(typeof d=="object"&&d.toJSON)i[n]=d.toJSON(null,p);else if(n==="source"){let h=p.get(d.input);h==null&&(h=o,p.set(d.input,o),o++),i[n]={end:d.end,inputId:h,start:d.start}}else i[n]=d}return c&&(i.inputs=[...p.keys()].map(n=>n.toJSON())),i}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(a=s){a.stringify&&(a=a.stringify);let p="";return a(this,i=>{p+=i}),p}warn(a,p,i){let c={node:this};for(let o in i)c[o]=i[o];return a.warn(p,c)}get proxyOf(){return this}}return Zt=u,u.default=u,Zt}var er,Ds;function yt(){if(Ds)return er;Ds=1;let e=gt();class t extends e{constructor(l){l&&typeof l.value<"u"&&typeof l.value!="string"&&(l={...l,value:String(l.value)}),super(l),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return er=t,t.default=t,er}var tr,Ts;function vo(){if(Ts)return tr;Ts=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return tr={nanoid:(l=21)=>{let s="",f=l;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(l,s=21)=>(f=s)=>{let u="",m=f;for(;m--;)u+=l[Math.random()*l.length|0];return u}},tr}var rr,Fs;function Li(){if(Fs)return rr;Fs=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{existsSync:r,readFileSync:l}=fe,{dirname:s,join:f}=fe;function u(a){return Buffer?Buffer.from(a,"base64").toString():window.atob(a)}class m{constructor(p,i){if(i.map===!1)return;this.loadAnnotation(p),this.inline=this.startWith(this.annotation,"data:");let c=i.map?i.map.prev:void 0,o=this.loadMap(i.from,c);!this.mapFile&&i.from&&(this.mapFile=i.from),this.mapFile&&(this.root=s(this.mapFile)),o&&(this.text=o)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(p){let i=/^data:application\/json;charset=utf-?8;base64,/,c=/^data:application\/json;base64,/,o=/^data:application\/json;charset=utf-?8,/,n=/^data:application\/json,/;if(o.test(p)||n.test(p))return decodeURIComponent(p.substr(RegExp.lastMatch.length));if(i.test(p)||c.test(p))return u(p.substr(RegExp.lastMatch.length));let d=p.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+d)}getAnnotationURL(p){return p.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(p){return typeof p!="object"?!1:typeof p.mappings=="string"||typeof p._mappings=="string"||Array.isArray(p.sections)}loadAnnotation(p){let i=p.match(/\/\*\s*# sourceMappingURL=/gm);if(!i)return;let c=p.lastIndexOf(i.pop()),o=p.indexOf("*/",c);c>-1&&o>-1&&(this.annotation=this.getAnnotationURL(p.substring(c,o)))}loadFile(p){if(this.root=s(p),r(p))return this.mapFile=p,l(p,"utf-8").toString().trim()}loadMap(p,i){if(i===!1)return!1;if(i){if(typeof i=="string")return i;if(typeof i=="function"){let c=i(p);if(c){let o=this.loadFile(c);if(!o)throw new Error("Unable to load previous source map: "+c.toString());return o}}else{if(i instanceof e)return t.fromSourceMap(i).toString();if(i instanceof t)return i.toString();if(this.isMap(i))return JSON.stringify(i);throw new Error("Unsupported previous source map format: "+i.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let c=this.annotation;return p&&(c=f(s(p),c)),this.loadFile(c)}}}startWith(p,i){return p?p.substr(0,i.length)===i:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return rr=m,m.default=m,rr}var sr,Us;function wt(){if(Us)return sr;Us=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{fileURLToPath:r,pathToFileURL:l}=fe,{isAbsolute:s,resolve:f}=fe,{nanoid:u}=vo(),m=fe,a=$r(),p=Li(),i=Symbol("fromOffsetCache"),c=!!(e&&t),o=!!(f&&s);class n{constructor(h,y={}){if(h===null||typeof h>"u"||typeof h=="object"&&!h.toString)throw new Error(`PostCSS received ${h} instead of CSS string`);if(this.css=h.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,y.from&&(!o||/^\w+:\/\//.test(y.from)||s(y.from)?this.file=y.from:this.file=f(y.from)),o&&c){let C=new p(this.css,y);if(C.text){this.map=C;let w=C.consumer().file;!this.file&&w&&(this.file=this.mapResolve(w))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(h,y,C,w={}){let S,b,g;if(y&&typeof y=="object"){let x=y,O=C;if(typeof x.offset=="number"){let A=this.fromOffset(x.offset);y=A.line,C=A.col}else y=x.line,C=x.column;if(typeof O.offset=="number"){let A=this.fromOffset(O.offset);b=A.line,g=A.col}else b=O.line,g=O.column}else if(!C){let x=this.fromOffset(y);y=x.line,C=x.col}let v=this.origin(y,C,b,g);return v?S=new a(h,v.endLine===void 0?v.line:{column:v.column,line:v.line},v.endLine===void 0?v.column:{column:v.endColumn,line:v.endLine},v.source,v.file,w.plugin):S=new a(h,b===void 0?y:{column:C,line:y},b===void 0?C:{column:g,line:b},this.css,this.file,w.plugin),S.input={column:C,endColumn:g,endLine:b,line:y,source:this.css},this.file&&(l&&(S.input.url=l(this.file).toString()),S.input.file=this.file),S}fromOffset(h){let y,C;if(this[i])C=this[i];else{let S=this.css.split(` -`);C=new Array(S.length);let b=0;for(let g=0,v=S.length;g=y)w=C.length-1;else{let S=C.length-2,b;for(;w>1),h=C[b+1])w=b+1;else{w=b;break}}return{col:h-C[w]+1,line:w+1}}mapResolve(h){return/^\w+:\/\//.test(h)?h:f(this.map.consumer().sourceRoot||this.map.root||".",h)}origin(h,y,C,w){if(!this.map)return!1;let S=this.map.consumer(),b=S.originalPositionFor({column:y,line:h});if(!b.source)return!1;let g;typeof C=="number"&&(g=S.originalPositionFor({column:w,line:C}));let v;s(b.source)?v=l(b.source):v=new URL(b.source,this.map.consumer().sourceRoot||l(this.map.mapFile));let x={column:b.column,endColumn:g&&g.column,endLine:g&&g.line,line:b.line,url:v.toString()};if(v.protocol==="file:")if(r)x.file=r(v);else throw new Error("file: protocol is not available in this PostCSS build");let O=S.sourceContentFor(b.source);return O&&(x.source=O),x}toJSON(){let h={};for(let y of["hasBOM","css","file","id"])this[y]!=null&&(h[y]=this[y]);return this.map&&(h.map={...this.map},h.map.consumerCache&&(h.map.consumerCache=void 0)),h}get from(){return this.file||this.id}}return sr=n,n.default=n,m&&m.registerInput&&m.registerInput(n),sr}var ir,$s;function Di(){if($s)return ir;$s=1;let{SourceMapConsumer:e,SourceMapGenerator:t}=fe,{dirname:r,relative:l,resolve:s,sep:f}=fe,{pathToFileURL:u}=fe,m=wt(),a=!!(e&&t),p=!!(r&&s&&l&&f);class i{constructor(o,n,d,h){this.stringify=o,this.mapOpts=d.map||{},this.root=n,this.opts=d,this.css=h,this.originalCSS=h,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let o;this.isInline()?o="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?o=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?o=this.mapOpts.annotation(this.opts.to,this.root):o=this.outputFile()+".map";let n=` -`;this.css.includes(`\r -`)&&(n=`\r -`),this.css+=n+"/*# sourceMappingURL="+o+" */"}applyPrevMaps(){for(let o of this.previous()){let n=this.toUrl(this.path(o.file)),d=o.root||r(o.file),h;this.mapOpts.sourcesContent===!1?(h=new e(o.text),h.sourcesContent&&(h.sourcesContent=null)):h=o.consumer(),this.map.applySourceMap(h,n,this.toUrl(this.path(d)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let o;for(let n=this.root.nodes.length-1;n>=0;n--)o=this.root.nodes[n],o.type==="comment"&&o.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(n)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),p&&a&&this.isMap())return this.generateMap();{let o="";return this.stringify(this.root,n=>{o+=n}),[o]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let o=this.previous()[0].consumer();o.file=this.outputFile(),this.map=t.fromSourceMap(o,{ignoreInvalidMapping:!0})}else this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new t({file:this.outputFile(),ignoreInvalidMapping:!0});let o=1,n=1,d="",h={generated:{column:0,line:0},original:{column:0,line:0},source:""},y,C;this.stringify(this.root,(w,S,b)=>{if(this.css+=w,S&&b!=="end"&&(h.generated.line=o,h.generated.column=n-1,S.source&&S.source.start?(h.source=this.sourcePath(S),h.original.line=S.source.start.line,h.original.column=S.source.start.column-1,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,this.map.addMapping(h))),y=w.match(/\n/g),y?(o+=y.length,C=w.lastIndexOf(` -`),n=w.length-C):n+=w.length,S&&b!=="start"){let g=S.parent||{raws:{}};(!(S.type==="decl"||S.type==="atrule"&&!S.nodes)||S!==g.last||g.raws.semicolon)&&(S.source&&S.source.end?(h.source=this.sourcePath(S),h.original.line=S.source.end.line,h.original.column=S.source.end.column-1,h.generated.line=o,h.generated.column=n-2,this.map.addMapping(h)):(h.source=d,h.original.line=1,h.original.column=0,h.generated.line=o,h.generated.column=n-1,this.map.addMapping(h)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(o=>o.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let o=this.mapOpts.annotation;return typeof o<"u"&&o!==!0?!1:this.previous().length?this.previous().some(n=>n.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(o=>o.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(o){if(this.mapOpts.absolute||o.charCodeAt(0)===60||/^\w+:\/\//.test(o))return o;let n=this.memoizedPaths.get(o);if(n)return n;let d=this.opts.to?r(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(d=r(s(d,this.mapOpts.annotation)));let h=l(d,o);return this.memoizedPaths.set(o,h),h}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(o=>{if(o.source&&o.source.input.map){let n=o.source.input.map;this.previousMaps.includes(n)||this.previousMaps.push(n)}});else{let o=new m(this.originalCSS,this.opts);o.map&&this.previousMaps.push(o.map)}return this.previousMaps}setSourcesContent(){let o={};if(this.root)this.root.walk(n=>{if(n.source){let d=n.source.input.from;if(d&&!o[d]){o[d]=!0;let h=this.usesFileUrls?this.toFileUrl(d):this.toUrl(this.path(d));this.map.setSourceContent(h,n.source.input.css)}}});else if(this.css){let n=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(n,this.css)}}sourcePath(o){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(o.source.input.from):this.toUrl(this.path(o.source.input.from))}toBase64(o){return Buffer?Buffer.from(o).toString("base64"):window.btoa(unescape(encodeURIComponent(o)))}toFileUrl(o){let n=this.memoizedFileURLs.get(o);if(n)return n;if(u){let d=u(o).toString();return this.memoizedFileURLs.set(o,d),d}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(o){let n=this.memoizedURLs.get(o);if(n)return n;f==="\\"&&(o=o.replace(/\\/g,"/"));let d=encodeURI(o).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(o,d),d}}return ir=i,ir}var nr,Bs;function bt(){if(Bs)return nr;Bs=1;let e=gt();class t extends e{constructor(l){super(l),this.type="comment"}}return nr=t,t.default=t,nr}var or,zs;function Re(){if(zs)return or;zs=1;let{isClean:e,my:t}=Br(),r=yt(),l=bt(),s=gt(),f,u,m,a;function p(o){return o.map(n=>(n.nodes&&(n.nodes=p(n.nodes)),delete n.source,n))}function i(o){if(o[e]=!1,o.proxyOf.nodes)for(let n of o.proxyOf.nodes)i(n)}class c extends s{append(...n){for(let d of n){let h=this.normalize(d,this.last);for(let y of h)this.proxyOf.nodes.push(y)}return this.markDirty(),this}cleanRaws(n){if(super.cleanRaws(n),this.nodes)for(let d of this.nodes)d.cleanRaws(n)}each(n){if(!this.proxyOf.nodes)return;let d=this.getIterator(),h,y;for(;this.indexes[d]n[d](...h.map(y=>typeof y=="function"?(C,w)=>y(C.toProxy(),w):y)):d==="every"||d==="some"?h=>n[d]((y,...C)=>h(y.toProxy(),...C)):d==="root"?()=>n.root().toProxy():d==="nodes"?n.nodes.map(h=>h.toProxy()):d==="first"||d==="last"?n[d].toProxy():n[d]:n[d]},set(n,d,h){return n[d]===h||(n[d]=h,(d==="name"||d==="params"||d==="selector")&&n.markDirty()),!0}}}index(n){return typeof n=="number"?n:(n.proxyOf&&(n=n.proxyOf),this.proxyOf.nodes.indexOf(n))}insertAfter(n,d){let h=this.index(n),y=this.normalize(d,this.proxyOf.nodes[h]).reverse();h=this.index(n);for(let w of y)this.proxyOf.nodes.splice(h+1,0,w);let C;for(let w in this.indexes)C=this.indexes[w],h"u")n=[];else if(Array.isArray(n)){n=n.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type==="root"&&this.type!=="document"){n=n.nodes.slice(0);for(let y of n)y.parent&&y.parent.removeChild(y,"ignore")}else if(n.type)n=[n];else if(n.prop){if(typeof n.value>"u")throw new Error("Value field is missed in node creation");typeof n.value!="string"&&(n.value=String(n.value)),n=[new r(n)]}else if(n.selector)n=[new u(n)];else if(n.name)n=[new m(n)];else if(n.text)n=[new l(n)];else throw new Error("Unknown node type in node creation");return n.map(y=>(y[t]||c.rebuild(y),y=y.proxyOf,y.parent&&y.parent.removeChild(y),y[e]&&i(y),typeof y.raws.before>"u"&&d&&typeof d.raws.before<"u"&&(y.raws.before=d.raws.before.replace(/\S/g,"")),y.parent=this.proxyOf,y))}prepend(...n){n=n.reverse();for(let d of n){let h=this.normalize(d,this.first,"prepend").reverse();for(let y of h)this.proxyOf.nodes.unshift(y);for(let y in this.indexes)this.indexes[y]=this.indexes[y]+h.length}return this.markDirty(),this}push(n){return n.parent=this,this.proxyOf.nodes.push(n),this}removeAll(){for(let n of this.proxyOf.nodes)n.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(n){n=this.index(n),this.proxyOf.nodes[n].parent=void 0,this.proxyOf.nodes.splice(n,1);let d;for(let h in this.indexes)d=this.indexes[h],d>=n&&(this.indexes[h]=d-1);return this.markDirty(),this}replaceValues(n,d,h){return h||(h=d,d={}),this.walkDecls(y=>{d.props&&!d.props.includes(y.prop)||d.fast&&!y.value.includes(d.fast)||(y.value=y.value.replace(n,h))}),this.markDirty(),this}some(n){return this.nodes.some(n)}walk(n){return this.each((d,h)=>{let y;try{y=n(d,h)}catch(C){throw d.addToError(C)}return y!==!1&&d.walk&&(y=d.walk(n)),y})}walkAtRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="atrule"&&n.test(h.name))return d(h,y)}):this.walk((h,y)=>{if(h.type==="atrule"&&h.name===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="atrule")return d(h,y)}))}walkComments(n){return this.walk((d,h)=>{if(d.type==="comment")return n(d,h)})}walkDecls(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="decl"&&n.test(h.prop))return d(h,y)}):this.walk((h,y)=>{if(h.type==="decl"&&h.prop===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="decl")return d(h,y)}))}walkRules(n,d){return d?n instanceof RegExp?this.walk((h,y)=>{if(h.type==="rule"&&n.test(h.selector))return d(h,y)}):this.walk((h,y)=>{if(h.type==="rule"&&h.selector===n)return d(h,y)}):(d=n,this.walk((h,y)=>{if(h.type==="rule")return d(h,y)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return c.registerParse=o=>{f=o},c.registerRule=o=>{u=o},c.registerAtRule=o=>{m=o},c.registerRoot=o=>{a=o},or=c,c.default=c,c.rebuild=o=>{o.type==="atrule"?Object.setPrototypeOf(o,m.prototype):o.type==="rule"?Object.setPrototypeOf(o,u.prototype):o.type==="decl"?Object.setPrototypeOf(o,r.prototype):o.type==="comment"?Object.setPrototypeOf(o,l.prototype):o.type==="root"&&Object.setPrototypeOf(o,a.prototype),o[t]=!0,o.nodes&&o.nodes.forEach(n=>{c.rebuild(n)})},or}var ar,Ws;function zr(){if(Ws)return ar;Ws=1;let e=Re(),t,r;class l extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},ar=l,l.default=l,ar}var lr,qs;function Ti(){if(qs)return lr;qs=1;class e{constructor(r,l={}){if(this.type="warning",this.text=r,l.node&&l.node.source){let s=l.node.rangeBy(l);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in l)this[s]=l[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return lr=e,e.default=e,lr}var ur,js;function Wr(){if(js)return ur;js=1;let e=Ti();class t{constructor(l,s,f){this.processor=l,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(l,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(l,s);return this.messages.push(f),f}warnings(){return this.messages.filter(l=>l.type==="warning")}get content(){return this.css}}return ur=t,t.default=t,ur}var cr,Hs;function Co(){if(Hs)return cr;Hs=1;const e=39,t=34,r=92,l=47,s=10,f=32,u=12,m=9,a=13,p=91,i=93,c=40,o=41,n=123,d=125,h=59,y=42,C=58,w=64,S=/[\t\n\f\r "#'()/;[\\\]{}]/g,b=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,g=/.[\r\n"'(/\\]/,v=/[\da-f]/i;return cr=function(O,A={}){let M=O.css.valueOf(),$=A.ignoreErrors,k,R,Z,N,ee,H,F,Q,W,P,ye=M.length,E=0,pe=[],se=[];function Me(){return E}function de(X){throw O.error("Unclosed "+X,E)}function De(){return se.length===0&&E>=ye}function ae(X){if(se.length)return se.pop();if(E>=ye)return;let ne=X?X.ignoreUnclosed:!1;switch(k=M.charCodeAt(E),k){case s:case f:case m:case a:case u:{R=E;do R+=1,k=M.charCodeAt(R);while(k===f||k===s||k===m||k===a||k===u);P=["space",M.slice(E,R)],E=R-1;break}case p:case i:case n:case d:case C:case h:case o:{let _=String.fromCharCode(k);P=[_,_,E];break}case c:{if(Q=pe.length?pe.pop()[1]:"",W=M.charCodeAt(E+1),Q==="url"&&W!==e&&W!==t&&W!==f&&W!==s&&W!==m&&W!==u&&W!==a){R=E;do{if(H=!1,R=M.indexOf(")",R+1),R===-1)if($||ne){R=E;break}else de("bracket");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["brackets",M.slice(E,R+1),E,R],E=R}else R=M.indexOf(")",E+1),N=M.slice(E,R+1),R===-1||g.test(N)?P=["(","(",E]:(P=["brackets",N,E,R],E=R);break}case e:case t:{Z=k===e?"'":'"',R=E;do{if(H=!1,R=M.indexOf(Z,R+1),R===-1)if($||ne){R=E+1;break}else de("string");for(F=R;M.charCodeAt(F-1)===r;)F-=1,H=!H}while(H);P=["string",M.slice(E,R+1),E,R],E=R;break}case w:{S.lastIndex=E+1,S.test(M),S.lastIndex===0?R=M.length-1:R=S.lastIndex-2,P=["at-word",M.slice(E,R+1),E,R],E=R;break}case r:{for(R=E,ee=!0;M.charCodeAt(R+1)===r;)R+=1,ee=!ee;if(k=M.charCodeAt(R+1),ee&&k!==l&&k!==f&&k!==s&&k!==m&&k!==a&&k!==u&&(R+=1,v.test(M.charAt(R)))){for(;v.test(M.charAt(R+1));)R+=1;M.charCodeAt(R+1)===f&&(R+=1)}P=["word",M.slice(E,R+1),E,R],E=R;break}default:{k===l&&M.charCodeAt(E+1)===y?(R=M.indexOf("*/",E+2)+1,R===0&&($||ne?R=M.length:de("comment")),P=["comment",M.slice(E,R+1),E,R],E=R):(b.lastIndex=E+1,b.test(M),b.lastIndex===0?R=M.length-1:R=b.lastIndex-2,P=["word",M.slice(E,R+1),E,R],pe.push(P),E=R);break}}return E++,P}function le(X){se.push(X)}return{back:le,endOfFile:De,nextToken:ae,position:Me}},cr}var hr,Gs;function qr(){if(Gs)return hr;Gs=1;let e=Re();class t extends e{constructor(l){super(l),this.type="atrule"}append(...l){return this.proxyOf.nodes||(this.nodes=[]),super.append(...l)}prepend(...l){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...l)}}return hr=t,t.default=t,e.registerAtRule(t),hr}var fr,Vs;function We(){if(Vs)return fr;Vs=1;let e=Re(),t,r;class l extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,u,m){let a=super.normalize(f);if(u){if(m==="prepend")this.nodes.length>1?u.raws.before=this.nodes[1].raws.before:delete u.raws.before;else if(this.first!==u)for(let p of a)p.raws.before=u.raws.before}return a}removeChild(f,u){let m=this.index(f);return!u&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new t(new r,this,f).stringify()}}return l.registerLazyResult=s=>{t=s},l.registerProcessor=s=>{r=s},fr=l,l.default=l,e.registerRoot(l),fr}var pr,Js;function Fi(){if(Js)return pr;Js=1;let e={comma(t){return e.split(t,[","],!0)},space(t){let r=[" ",` -`," "];return e.split(t,r)},split(t,r,l){let s=[],f="",u=!1,m=0,a=!1,p="",i=!1;for(let c of t)i?i=!1:c==="\\"?i=!0:a?c===p&&(a=!1):c==='"'||c==="'"?(a=!0,p=c):c==="("?m+=1:c===")"?m>0&&(m-=1):m===0&&r.includes(c)&&(u=!0),u?(f!==""&&s.push(f.trim()),f="",u=!1):f+=c;return(l||f!=="")&&s.push(f.trim()),s}};return pr=e,e.default=e,pr}var dr,Ys;function jr(){if(Ys)return dr;Ys=1;let e=Re(),t=Fi();class r extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return t.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,u=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(u)}}return dr=r,r.default=r,e.registerRule(r),dr}var mr,Qs;function xo(){if(Qs)return mr;Qs=1;let e=yt(),t=Co(),r=bt(),l=qr(),s=We(),f=jr();const u={empty:!0,space:!0};function m(p){for(let i=p.length-1;i>=0;i--){let c=p[i],o=c[3]||c[2];if(o)return o}}class a{constructor(i){this.input=i,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:i,start:{column:1,line:1,offset:0}}}atrule(i){let c=new l;c.name=i[1].slice(1),c.name===""&&this.unnamedAtrule(c,i),this.init(c,i[2]);let o,n,d,h=!1,y=!1,C=[],w=[];for(;!this.tokenizer.endOfFile();){if(i=this.tokenizer.nextToken(),o=i[0],o==="("||o==="["?w.push(o==="("?")":"]"):o==="{"&&w.length>0?w.push("}"):o===w[w.length-1]&&w.pop(),w.length===0)if(o===";"){c.source.end=this.getPosition(i[2]),c.source.end.offset++,this.semicolon=!0;break}else if(o==="{"){y=!0;break}else if(o==="}"){if(C.length>0){for(d=C.length-1,n=C[d];n&&n[0]==="space";)n=C[--d];n&&(c.source.end=this.getPosition(n[3]||n[2]),c.source.end.offset++)}this.end(i);break}else C.push(i);else C.push(i);if(this.tokenizer.endOfFile()){h=!0;break}}c.raws.between=this.spacesAndCommentsFromEnd(C),C.length?(c.raws.afterName=this.spacesAndCommentsFromStart(C),this.raw(c,"params",C),h&&(i=C[C.length-1],c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++,this.spaces=c.raws.between,c.raws.between="")):(c.raws.afterName="",c.params=""),y&&(c.nodes=[],this.current=c)}checkMissedSemicolon(i){let c=this.colon(i);if(c===!1)return;let o=0,n;for(let d=c-1;d>=0&&(n=i[d],!(n[0]!=="space"&&(o+=1,o===2)));d--);throw this.input.error("Missed semicolon",n[0]==="word"?n[3]+1:n[2])}colon(i){let c=0,o,n,d;for(let[h,y]of i.entries()){if(o=y,n=o[0],n==="("&&(c+=1),n===")"&&(c-=1),c===0&&n===":")if(!d)this.doubleColon(o);else{if(d[0]==="word"&&d[1]==="progid")continue;return h}d=o}return!1}comment(i){let c=new r;this.init(c,i[2]),c.source.end=this.getPosition(i[3]||i[2]),c.source.end.offset++;let o=i[1].slice(2,-2);if(/^\s*$/.test(o))c.text="",c.raws.left=o,c.raws.right="";else{let n=o.match(/^(\s*)([^]*\S)(\s*)$/);c.text=n[2],c.raws.left=n[1],c.raws.right=n[3]}}createTokenizer(){this.tokenizer=t(this.input)}decl(i,c){let o=new e;this.init(o,i[0][2]);let n=i[i.length-1];for(n[0]===";"&&(this.semicolon=!0,i.pop()),o.source.end=this.getPosition(n[3]||n[2]||m(i)),o.source.end.offset++;i[0][0]!=="word";)i.length===1&&this.unknownWord(i),o.raws.before+=i.shift()[1];for(o.source.start=this.getPosition(i[0][2]),o.prop="";i.length;){let w=i[0][0];if(w===":"||w==="space"||w==="comment")break;o.prop+=i.shift()[1]}o.raws.between="";let d;for(;i.length;)if(d=i.shift(),d[0]===":"){o.raws.between+=d[1];break}else d[0]==="word"&&/\w/.test(d[1])&&this.unknownWord([d]),o.raws.between+=d[1];(o.prop[0]==="_"||o.prop[0]==="*")&&(o.raws.before+=o.prop[0],o.prop=o.prop.slice(1));let h=[],y;for(;i.length&&(y=i[0][0],!(y!=="space"&&y!=="comment"));)h.push(i.shift());this.precheckMissedSemicolon(i);for(let w=i.length-1;w>=0;w--){if(d=i[w],d[1].toLowerCase()==="!important"){o.important=!0;let S=this.stringFrom(i,w);S=this.spacesFromEnd(i)+S,S!==" !important"&&(o.raws.important=S);break}else if(d[1].toLowerCase()==="important"){let S=i.slice(0),b="";for(let g=w;g>0;g--){let v=S[g][0];if(b.trim().indexOf("!")===0&&v!=="space")break;b=S.pop()[1]+b}b.trim().indexOf("!")===0&&(o.important=!0,o.raws.important=b,i=S)}if(d[0]!=="space"&&d[0]!=="comment")break}i.some(w=>w[0]!=="space"&&w[0]!=="comment")&&(o.raws.between+=h.map(w=>w[1]).join(""),h=[]),this.raw(o,"value",h.concat(i),c),o.value.includes(":")&&!c&&this.checkMissedSemicolon(i)}doubleColon(i){throw this.input.error("Double colon",{offset:i[2]},{offset:i[2]+i[1].length})}emptyRule(i){let c=new f;this.init(c,i[2]),c.selector="",c.raws.between="",this.current=c}end(i){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(i[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(i)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(i){if(this.spaces+=i[1],this.current.nodes){let c=this.current.nodes[this.current.nodes.length-1];c&&c.type==="rule"&&!c.raws.ownSemicolon&&(c.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(i){let c=this.input.fromOffset(i);return{column:c.col,line:c.line,offset:i}}init(i,c){this.current.push(i),i.source={input:this.input,start:this.getPosition(c)},i.raws.before=this.spaces,this.spaces="",i.type!=="comment"&&(this.semicolon=!1)}other(i){let c=!1,o=null,n=!1,d=null,h=[],y=i[1].startsWith("--"),C=[],w=i;for(;w;){if(o=w[0],C.push(w),o==="("||o==="[")d||(d=w),h.push(o==="("?")":"]");else if(y&&n&&o==="{")d||(d=w),h.push("}");else if(h.length===0)if(o===";")if(n){this.decl(C,y);return}else break;else if(o==="{"){this.rule(C);return}else if(o==="}"){this.tokenizer.back(C.pop()),c=!0;break}else o===":"&&(n=!0);else o===h[h.length-1]&&(h.pop(),h.length===0&&(d=null));w=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(c=!0),h.length>0&&this.unclosedBracket(d),c&&n){if(!y)for(;C.length&&(w=C[C.length-1][0],!(w!=="space"&&w!=="comment"));)this.tokenizer.back(C.pop());this.decl(C,y)}else this.unknownWord(C)}parse(){let i;for(;!this.tokenizer.endOfFile();)switch(i=this.tokenizer.nextToken(),i[0]){case"space":this.spaces+=i[1];break;case";":this.freeSemicolon(i);break;case"}":this.end(i);break;case"comment":this.comment(i);break;case"at-word":this.atrule(i);break;case"{":this.emptyRule(i);break;default:this.other(i);break}this.endFile()}precheckMissedSemicolon(){}raw(i,c,o,n){let d,h,y=o.length,C="",w=!0,S,b;for(let g=0;gv+x[1],"");i.raws[c]={raw:g,value:C}}i[c]=C}rule(i){i.pop();let c=new f;this.init(c,i[0][2]),c.raws.between=this.spacesAndCommentsFromEnd(i),this.raw(c,"selector",i),this.current=c}spacesAndCommentsFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],!(c!=="space"&&c!=="comment"));)o=i.pop()[1]+o;return o}spacesAndCommentsFromStart(i){let c,o="";for(;i.length&&(c=i[0][0],!(c!=="space"&&c!=="comment"));)o+=i.shift()[1];return o}spacesFromEnd(i){let c,o="";for(;i.length&&(c=i[i.length-1][0],c==="space");)o=i.pop()[1]+o;return o}stringFrom(i,c){let o="";for(let n=c;ny(b)),S}let C={};class w{constructor(b,g,v){this.stringified=!1,this.processed=!1;let x;if(typeof g=="object"&&g!==null&&(g.type==="root"||g.type==="document"))x=y(g);else if(g instanceof w||g instanceof u)x=y(g.root),g.map&&(typeof v.map>"u"&&(v.map={}),v.map.inline||(v.map.inline=!1),v.map.prev=g.map);else{let O=m;v.syntax&&(O=v.syntax.parse),v.parser&&(O=v.parser),O.parse&&(O=O.parse);try{x=O(g,v)}catch(A){this.processed=!0,this.error=A}x&&!x[t]&&s.rebuild(x)}this.result=new u(b,x,v),this.helpers={...C,postcss:C,result:this.result},this.plugins=this.processor.plugins.map(O=>typeof O=="object"&&O.prepare?{...O,...O.prepare(this.result)}:O)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(b){return this.async().catch(b)}finally(b){return this.async().then(b,b)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(b,g){let v=this.result.lastPlugin;try{g&&g.addToError(b),this.error=b,b.name==="CssSyntaxError"&&!b.plugin?(b.plugin=v.postcssPlugin,b.setMessage()):v.postcssVersion}catch(x){console&&console.error&&console.error(x)}return b}prepareVisitors(){this.listeners={};let b=(g,v,x)=>{this.listeners[v]||(this.listeners[v]=[]),this.listeners[v].push([g,x])};for(let g of this.plugins)if(typeof g=="object")for(let v in g){if(!i[v]&&/^[A-Z]/.test(v))throw new Error(`Unknown event ${v} in ${g.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!c[v])if(typeof g[v]=="object")for(let x in g[v])x==="*"?b(g,v,g[v][x]):b(g,v+"-"+x.toLowerCase(),g[v][x]);else typeof g[v]=="function"&&b(g,v,g[v])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let b=0;b0;){let v=this.visitTick(g);if(n(v))try{await v}catch(x){let O=g[g.length-1].node;throw this.handleError(x,O)}}}if(this.listeners.OnceExit)for(let[g,v]of this.listeners.OnceExit){this.result.lastPlugin=g;try{if(b.type==="document"){let x=b.nodes.map(O=>v(O,this.helpers));await Promise.all(x)}else await v(b,this.helpers)}catch(x){throw this.handleError(x)}}}return this.processed=!0,this.stringify()}runOnRoot(b){this.result.lastPlugin=b;try{if(typeof b=="object"&&b.Once){if(this.result.root.type==="document"){let g=this.result.root.nodes.map(v=>b.Once(v,this.helpers));return n(g[0])?Promise.all(g):g}return b.Once(this.result.root,this.helpers)}else if(typeof b=="function")return b(this.result.root,this.result)}catch(g){throw this.handleError(g)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let b=this.result.opts,g=l;b.syntax&&(g=b.syntax.stringify),b.stringifier&&(g=b.stringifier),g.stringify&&(g=g.stringify);let x=new r(g,this.result.root,this.result.opts).generate();return this.result.css=x[0],this.result.map=x[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let b of this.plugins){let g=this.runOnRoot(b);if(n(g))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let b=this.result.root;for(;!b[e];)b[e]=!0,this.walkSync(b);if(this.listeners.OnceExit)if(b.type==="document")for(let g of b.nodes)this.visitSync(this.listeners.OnceExit,g);else this.visitSync(this.listeners.OnceExit,b)}return this.result}then(b,g){return this.async().then(b,g)}toString(){return this.css}visitSync(b,g){for(let[v,x]of b){this.result.lastPlugin=v;let O;try{O=x(g,this.helpers)}catch(A){throw this.handleError(A,g.proxyOf)}if(g.type!=="root"&&g.type!=="document"&&!g.parent)return!0;if(n(O))throw this.getAsyncError()}}visitTick(b){let g=b[b.length-1],{node:v,visitors:x}=g;if(v.type!=="root"&&v.type!=="document"&&!v.parent){b.pop();return}if(x.length>0&&g.visitorIndex{x[e]||this.walkSync(x)});else{let x=this.listeners[v];if(x&&this.visitSync(x,b.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return w.registerPostcss=S=>{C=S},yr=w,w.default=w,a.registerLazyResult(w),f.registerLazyResult(w),yr}var wr,Zs;function Ro(){if(Zs)return wr;Zs=1;let e=Di(),t=mt(),r=Hr();const l=Wr();class s{constructor(u,m,a){m=m.toString(),this.stringified=!1,this._processor=u,this._css=m,this._opts=a,this._map=void 0;let p,i=t;this.result=new l(this._processor,p,this._opts),this.result.css=m;let c=this;Object.defineProperty(this.result,"root",{get(){return c.root}});let o=new e(i,p,this._opts,m);if(o.isMap()){let[n,d]=o.generate();n&&(this.result.css=n),d&&(this.result.map=d)}else o.clearAnnotation(),this.result.css=o.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(u){return this.async().catch(u)}finally(u){return this.async().then(u,u)}sync(){if(this.error)throw this.error;return this.result}then(u,m){return this.async().then(u,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let u,m=r;try{u=m(this._css,this._opts)}catch(a){this.error=a}if(this.error)throw this.error;return this._root=u,u}get[Symbol.toStringTag](){return"NoWorkResult"}}return wr=s,s.default=s,wr}var br,ei;function Oo(){if(ei)return br;ei=1;let e=Ro(),t=Ui(),r=zr(),l=We();class s{constructor(u=[]){this.version="8.4.38",this.plugins=this.normalize(u)}normalize(u){let m=[];for(let a of u)if(a.postcss===!0?a=a():a.postcss&&(a=a.postcss),typeof a=="object"&&Array.isArray(a.plugins))m=m.concat(a.plugins);else if(typeof a=="object"&&a.postcssPlugin)m.push(a);else if(typeof a=="function")m.push(a);else if(!(typeof a=="object"&&(a.parse||a.stringify)))throw new Error(a+" is not a PostCSS plugin");return m}process(u,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,u,m):new t(this,u,m)}use(u){return this.plugins=this.plugins.concat(this.normalize([u])),this}}return br=s,s.default=s,l.registerProcessor(s),r.registerProcessor(s),br}var Sr,ti;function Mo(){if(ti)return Sr;ti=1;let e=yt(),t=Li(),r=bt(),l=qr(),s=wt(),f=We(),u=jr();function m(a,p){if(Array.isArray(a))return a.map(o=>m(o));let{inputs:i,...c}=a;if(i){p=[];for(let o of i){let n={...o,__proto__:s.prototype};n.map&&(n.map={...n.map,__proto__:t.prototype}),p.push(n)}}if(c.nodes&&(c.nodes=a.nodes.map(o=>m(o,p))),c.source){let{inputId:o,...n}=c.source;c.source=n,o!=null&&(c.source.input=p[o])}if(c.type==="root")return new f(c);if(c.type==="decl")return new e(c);if(c.type==="rule")return new u(c);if(c.type==="comment")return new r(c);if(c.type==="atrule")return new l(c);throw new Error("Unknown node type: "+a.type)}return Sr=m,m.default=m,Sr}var vr,ri;function Eo(){if(ri)return vr;ri=1;let e=$r(),t=yt(),r=Ui(),l=Re(),s=Oo(),f=mt(),u=Mo(),m=zr(),a=Ti(),p=bt(),i=qr(),c=Wr(),o=wt(),n=Hr(),d=Fi(),h=jr(),y=We(),C=gt();function w(...S){return S.length===1&&Array.isArray(S[0])&&(S=S[0]),new s(S)}return w.plugin=function(b,g){let v=!1;function x(...A){console&&console.warn&&!v&&(v=!0,console.warn(b+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),rt.LANG&&rt.LANG.startsWith("cn")&&console.warn(b+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let M=g(...A);return M.postcssPlugin=b,M.postcssVersion=new s().version,M}let O;return Object.defineProperty(x,"postcss",{get(){return O||(O=x()),O}}),x.process=function(A,M,$){return w([x($)]).process(A,M)},x},w.stringify=f,w.parse=n,w.fromJSON=u,w.list=d,w.comment=S=>new p(S),w.atRule=S=>new i(S),w.decl=S=>new t(S),w.rule=S=>new h(S),w.root=S=>new y(S),w.document=S=>new m(S),w.CssSyntaxError=e,w.Declaration=t,w.Container=l,w.Processor=s,w.Document=m,w.Comment=p,w.Warning=a,w.AtRule=i,w.Result=c,w.Input=o,w.Rule=h,w.Root=y,w.Node=C,r.registerPostcss(w),vr=w,w.default=w,vr}var Io=Eo();const j=go(Io);j.stringify;j.fromJSON;j.plugin;j.parse;j.list;j.document;j.comment;j.atRule;j.rule;j.decl;j.root;j.CssSyntaxError;j.Declaration;j.Container;j.Processor;j.Document;j.Comment;j.Warning;j.AtRule;j.Result;j.Input;j.Rule;j.Root;j.Node;class Gr{constructor(...t){oe(this,"parentElement",null),oe(this,"parentNode",null),oe(this,"ownerDocument"),oe(this,"firstChild",null),oe(this,"lastChild",null),oe(this,"previousSibling",null),oe(this,"nextSibling",null),oe(this,"ELEMENT_NODE",1),oe(this,"TEXT_NODE",3),oe(this,"nodeType"),oe(this,"nodeName"),oe(this,"RRNodeType")}get childNodes(){const t=[];let r=this.firstChild;for(;r;)t.push(r),r=r.nextSibling;return t}contains(t){if(t instanceof Gr){if(t.ownerDocument!==this.ownerDocument)return!1;if(t===this)return!0}else return!1;for(;t.parentNode;){if(t.parentNode===this)return!0;t=t.parentNode}return!1}appendChild(t){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(t,r){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(t){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const si={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ii={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},Qe={};function Ao(e){var t,r;const l=(r=(t=globalThis?.Zone)==null?void 0:t.__symbol__)==null?void 0:r.call(t,e);if(l&&globalThis[l])return globalThis[l]}function Vr(e){if(Qe[e])return Qe[e];const t=Ao(e)||globalThis[e],r=t.prototype,l=e in si?si[e]:void 0,s=!!(l&&l.every(m=>{var a,p;return!!((p=(a=Object.getOwnPropertyDescriptor(r,m))==null?void 0:a.get)!=null&&p.toString().includes("[native code]"))})),f=e in ii?ii[e]:void 0,u=!!(f&&f.every(m=>{var a;return typeof r[m]=="function"&&((a=r[m])==null?void 0:a.toString().includes("[native code]"))}));if(s&&u)return Qe[e]=t.prototype,t.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const a=m.contentWindow;if(!a)return t.prototype;const p=a[e].prototype;return document.body.removeChild(m),p?Qe[e]=p:r}catch{return r}}const Cr={};function Se(e,t,r){var l;const s=`${e}.${String(r)}`;if(Cr[s])return Cr[s].call(t);const f=Vr(e),u=(l=Object.getOwnPropertyDescriptor(f,r))==null?void 0:l.get;return u?(Cr[s]=u,u.call(t)):t[r]}const xr={};function $i(e,t,r){const l=`${e}.${String(r)}`;if(xr[l])return xr[l].bind(t);const f=Vr(e)[r];return typeof f!="function"?t[r]:(xr[l]=f,f.bind(t))}function ko(e){return Se("Node",e,"childNodes")}function No(e){return Se("Node",e,"parentNode")}function Po(e){return Se("Node",e,"parentElement")}function _o(e){return Se("Node",e,"textContent")}function Lo(e,t){return $i("Node",e,"contains")(t)}function Do(e){return $i("Node",e,"getRootNode")()}function To(e){return!e||!("host"in e)?null:Se("ShadowRoot",e,"host")}function Fo(e){return e.styleSheets}function Uo(e){return!e||!("shadowRoot"in e)?null:Se("Element",e,"shadowRoot")}function $o(e,t){return Se("Element",e,"querySelector")(t)}function Bo(e,t){return Se("Element",e,"querySelectorAll")(t)}function Bi(){return Vr("MutationObserver").constructor}const L={childNodes:ko,parentNode:No,parentElement:Po,textContent:_o,contains:Lo,getRootNode:Do,host:To,styleSheets:Fo,shadowRoot:Uo,querySelector:$o,querySelectorAll:Bo,mutationObserver:Bi};function te(e,t,r=document){const l={capture:!0,passive:!0};return r.addEventListener(e,t,l),()=>r.removeEventListener(e,t,l)}const Ie=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let ni={map:{},getId(){return console.error(Ie),-1},getNode(){return console.error(Ie),null},removeNodeFromMap(){console.error(Ie)},has(){return console.error(Ie),!1},reset(){console.error(Ie)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(ni=new Proxy(ni,{get(e,t,r){return t==="map"&&console.error(Ie),Reflect.get(e,t,r)}}));function Be(e,t,r={}){let l=null,s=0;return function(...f){const u=Date.now();!s&&r.leading===!1&&(s=u);const m=t-(u-s),a=this;m<=0||m>t?(l&&(clearTimeout(l),l=null),s=u,e.apply(a,f)):!l&&r.trailing!==!1&&(l=setTimeout(()=>{s=r.leading===!1?0:Date.now(),l=null,e.apply(a,f)},m))}}function St(e,t,r,l,s=window){const f=s.Object.getOwnPropertyDescriptor(e,t);return s.Object.defineProperty(e,t,l?r:{set(u){setTimeout(()=>{r.set.call(this,u)},0),f&&f.set&&f.set.call(this,u)}}),()=>St(e,t,f||{},!0)}function Le(e,t,r){try{if(!(t in e))return()=>{};const l=e[t],s=r(l);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:l}})),e[t]=s,()=>{e[t]=l}}catch{return()=>{}}}let at=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(at=()=>new Date().getTime());function zi(e){var t,r,l,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((t=L.parentElement(f.body))==null?void 0:t.scrollLeft)||((r=f?.body)==null?void 0:r.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((l=L.parentElement(f.body))==null?void 0:l.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Wi(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function qi(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function ji(e){return e?e.nodeType===e.ELEMENT_NODE?e:L.parentElement(e):null}function re(e,t,r,l){if(!e)return!1;const s=ji(e);if(!s)return!1;try{if(typeof t=="string"){if(s.classList.contains(t)||l&&s.closest("."+t)!==null)return!0}else if(ot(s,t,l))return!0}catch{}return!!(r&&(s.matches(r)||l&&s.closest(r)!==null))}function zo(e,t){return t.getId(e)!==-1}function Rr(e,t,r){return e.tagName==="TITLE"&&r.headTitleMutations?!0:t.getId(e)===$e}function Hi(e,t){if(Te(e))return!1;const r=t.getId(e);if(!t.has(r))return!0;const l=L.parentNode(e);return l&&l.nodeType===e.DOCUMENT_NODE?!1:l?Hi(l,t):!0}function Er(e){return!!e.changedTouches}function Wo(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function Gi(e,t){return!!(e.nodeName==="IFRAME"&&t.getMeta(e))}function Vi(e,t){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&t.getMeta(e))}function Ir(e){return e?e instanceof Gr&&"shadowRoot"in e?!!e.shadowRoot:!!L.shadowRoot(e):!1}class qo{constructor(){I(this,"id",1),I(this,"styleIDMap",new WeakMap),I(this,"idStyleMap",new Map)}getId(t){return this.styleIDMap.get(t)??-1}has(t){return this.styleIDMap.has(t)}add(t,r){if(this.has(t))return this.getId(t);let l;return r===void 0?l=this.id++:l=r,this.styleIDMap.set(t,l),this.idStyleMap.set(l,t),l}getStyle(t){return this.idStyleMap.get(t)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function Ji(e){var t;let r=null;return"getRootNode"in e&&((t=L.getRootNode(e))==null?void 0:t.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&L.host(L.getRootNode(e))&&(r=L.host(L.getRootNode(e))),r}function jo(e){let t=e,r;for(;r=Ji(t);)t=r;return t}function Ho(e){const t=e.ownerDocument;if(!t)return!1;const r=jo(e);return L.contains(t,r)}function Yi(e){const t=e.ownerDocument;return t?L.contains(t,e)||Ho(e):!1}var U=(e=>(e[e.DomContentLoaded=0]="DomContentLoaded",e[e.Load=1]="Load",e[e.FullSnapshot=2]="FullSnapshot",e[e.IncrementalSnapshot=3]="IncrementalSnapshot",e[e.Meta=4]="Meta",e[e.Custom=5]="Custom",e[e.Plugin=6]="Plugin",e))(U||{}),D=(e=>(e[e.Mutation=0]="Mutation",e[e.MouseMove=1]="MouseMove",e[e.MouseInteraction=2]="MouseInteraction",e[e.Scroll=3]="Scroll",e[e.ViewportResize=4]="ViewportResize",e[e.Input=5]="Input",e[e.TouchMove=6]="TouchMove",e[e.MediaInteraction=7]="MediaInteraction",e[e.StyleSheetRule=8]="StyleSheetRule",e[e.CanvasMutation=9]="CanvasMutation",e[e.Font=10]="Font",e[e.Log=11]="Log",e[e.Drag=12]="Drag",e[e.StyleDeclaration=13]="StyleDeclaration",e[e.Selection=14]="Selection",e[e.AdoptedStyleSheet=15]="AdoptedStyleSheet",e[e.CustomElement=16]="CustomElement",e))(D||{}),ie=(e=>(e[e.MouseUp=0]="MouseUp",e[e.MouseDown=1]="MouseDown",e[e.Click=2]="Click",e[e.ContextMenu=3]="ContextMenu",e[e.DblClick=4]="DblClick",e[e.Focus=5]="Focus",e[e.Blur=6]="Blur",e[e.TouchStart=7]="TouchStart",e[e.TouchMove_Departed=8]="TouchMove_Departed",e[e.TouchEnd=9]="TouchEnd",e[e.TouchCancel=10]="TouchCancel",e))(ie||{}),ge=(e=>(e[e.Mouse=0]="Mouse",e[e.Pen=1]="Pen",e[e.Touch=2]="Touch",e))(ge||{}),_e=(e=>(e[e["2D"]=0]="2D",e[e.WebGL=1]="WebGL",e[e.WebGL2=2]="WebGL2",e))(_e||{}),Ae=(e=>(e[e.Play=0]="Play",e[e.Pause=1]="Pause",e[e.Seeked=2]="Seeked",e[e.VolumeChange=3]="VolumeChange",e[e.RateChange=4]="RateChange",e))(Ae||{}),Qi=(e=>(e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment",e))(Qi||{});function oi(e){return"__ln"in e}class Go{constructor(){I(this,"length",0),I(this,"head",null),I(this,"tail",null)}get(t){if(t>=this.length)throw new Error("Position outside of list range");let r=this.head;for(let l=0;l`${e}@${t}`;class Vo{constructor(){I(this,"frozen",!1),I(this,"locked",!1),I(this,"texts",[]),I(this,"attributes",[]),I(this,"attributeMap",new WeakMap),I(this,"removes",[]),I(this,"mapRemoves",[]),I(this,"movedMap",{}),I(this,"addedSet",new Set),I(this,"movedSet",new Set),I(this,"droppedSet",new Set),I(this,"removesSubTreeCache",new Set),I(this,"mutationCb"),I(this,"blockClass"),I(this,"blockSelector"),I(this,"maskTextClass"),I(this,"maskTextSelector"),I(this,"inlineStylesheet"),I(this,"maskInputOptions"),I(this,"maskTextFn"),I(this,"maskInputFn"),I(this,"maskAttributeFn"),I(this,"keepIframeSrcFn"),I(this,"recordCanvas"),I(this,"inlineImages"),I(this,"slimDOMOptions"),I(this,"dataURLOptions"),I(this,"doc"),I(this,"mirror"),I(this,"iframeManager"),I(this,"stylesheetManager"),I(this,"shadowDomManager"),I(this,"canvasManager"),I(this,"processedNodeManager"),I(this,"unattachedDoc"),I(this,"processMutations",t=>{t.forEach(this.processMutation),this.emit()}),I(this,"emit",()=>{if(this.frozen||this.locked)return;const t=[],r=new Set,l=new Go,s=a=>{let p=a,i=$e;for(;i===$e;)p=p&&p.nextSibling,i=p&&this.mirror.getId(p);return i},f=a=>{const p=L.parentNode(a);if(!p||!Yi(a))return;let i=!1;if(a.nodeType===Node.TEXT_NODE){const d=p.tagName;if(d==="TEXTAREA")return;d==="STYLE"&&this.addedSet.has(p)&&(i=!0)}const c=Te(p)?this.mirror.getId(Ji(a)):this.mirror.getId(p),o=s(a);if(c===-1||o===-1)return l.addNode(a);const n=Ne(a,{doc:this.doc,mirror:this.mirror,blockClass:this.blockClass,blockSelector:this.blockSelector,maskTextClass:this.maskTextClass,maskTextSelector:this.maskTextSelector,skipChild:!0,newlyAddedElement:!0,inlineStylesheet:this.inlineStylesheet,maskInputOptions:this.maskInputOptions,maskTextFn:this.maskTextFn,maskInputFn:this.maskInputFn,slimDOMOptions:this.slimDOMOptions,dataURLOptions:this.dataURLOptions,recordCanvas:this.recordCanvas,inlineImages:this.inlineImages,onSerialize:d=>{Gi(d,this.mirror)&&this.iframeManager.addIframe(d),Vi(d,this.mirror)&&this.stylesheetManager.trackLinkElement(d),Ir(a)&&this.shadowDomManager.addShadowRoot(L.shadowRoot(a),this.doc)},onIframeLoad:(d,h)=>{this.iframeManager.attachIframe(d,h),this.shadowDomManager.observeAttachShadow(d)},onStylesheetLoad:(d,h)=>{this.stylesheetManager.attachLinkElement(d,h)},cssCaptured:i});n&&(t.push({parentId:c,nextId:o,node:n}),r.add(n.id))};for(;this.mapRemoves.length;)this.mirror.removeNodeFromMap(this.mapRemoves.shift());for(const a of this.movedSet)li(this.removesSubTreeCache,a,this.mirror)&&!this.movedSet.has(L.parentNode(a))||f(a);for(const a of this.addedSet)!ui(this.droppedSet,a)&&!li(this.removesSubTreeCache,a,this.mirror)||ui(this.movedSet,a)?f(a):this.droppedSet.add(a);let u=null;for(;l.length;){let a=null;if(u){const p=this.mirror.getId(L.parentNode(u.value)),i=s(u.value);p!==-1&&i!==-1&&(a=u)}if(!a){let p=l.tail;for(;p;){const i=p;if(p=p.previous,i){const c=this.mirror.getId(L.parentNode(i.value));if(s(i.value)===-1)continue;if(c!==-1){a=i;break}else{const n=i.value,d=L.parentNode(n);if(d&&d.nodeType===Node.DOCUMENT_FRAGMENT_NODE){const h=L.host(d);if(this.mirror.getId(h)!==-1){a=i;break}}}}}}if(!a){for(;l.head;)l.removeNode(l.head.value);break}u=a.previous,l.removeNode(a.value),f(a.value)}const m={texts:this.texts.map(a=>{const p=a.node,i=L.parentNode(p);return i&&i.tagName==="TEXTAREA"&&this.genTextAreaValueMutation(i),{id:this.mirror.getId(p),value:a.value}}).filter(a=>!r.has(a.id)).filter(a=>this.mirror.has(a.id)),attributes:this.attributes.map(a=>{const{attributes:p}=a;if(typeof p.style=="string"){const i=JSON.stringify(a.styleDiff),c=JSON.stringify(a._unchangedStyles);i.length!r.has(a.id)).filter(a=>this.mirror.has(a.id)),removes:this.removes,adds:t};!m.texts.length&&!m.attributes.length&&!m.removes.length&&!m.adds.length||(this.texts=[],this.attributes=[],this.attributeMap=new WeakMap,this.removes=[],this.addedSet=new Set,this.movedSet=new Set,this.droppedSet=new Set,this.removesSubTreeCache=new Set,this.movedMap={},this.mutationCb(m))}),I(this,"genTextAreaValueMutation",t=>{let r=this.attributeMap.get(t);r||(r={node:t,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(r),this.attributeMap.set(t,r));const l=Array.from(L.childNodes(t),s=>L.textContent(s)||"").join("");r.attributes.value=st({element:t,maskInputOptions:this.maskInputOptions,tagName:t.tagName,type:it(t),value:l,maskInputFn:this.maskInputFn})}),I(this,"processMutation",t=>{if(!Rr(t.target,this.mirror,this.slimDOMOptions))switch(t.type){case"characterData":{const r=L.textContent(t.target);!re(t.target,this.blockClass,this.blockSelector,!1)&&r!==t.oldValue&&this.texts.push({value:Mi(t.target,this.maskTextClass,this.maskTextSelector,!0)&&r?this.maskTextFn?this.maskTextFn(r,ji(t.target)):r.replace(/[\S]/g,"*"):r,node:t.target});break}case"attributes":{const r=t.target;let l=t.attributeName,s=t.target.getAttribute(l);if(l==="value"){const u=it(r);s=st({element:r,maskInputOptions:this.maskInputOptions,tagName:r.tagName,type:u,value:s,maskInputFn:this.maskInputFn})}if(re(t.target,this.blockClass,this.blockSelector,!1)||s===t.oldValue)return;let f=this.attributeMap.get(t.target);if(r.tagName==="IFRAME"&&l==="src"&&!this.keepIframeSrcFn(s))if(!r.contentDocument)l="rr_src";else return;if(f||(f={node:t.target,attributes:{},styleDiff:{},_unchangedStyles:{}},this.attributes.push(f),this.attributeMap.set(t.target,f)),l==="type"&&r.tagName==="INPUT"&&(t.oldValue||"").toLowerCase()==="password"&&r.setAttribute("data-rr-is-password","true"),!Oi(r.tagName,l)){if(f.attributes[l]=Ri(this.doc,Ce(r.tagName),Ce(l),s),this.maskAttributeFn&&typeof f.attributes[l]=="string"){const u=this.maskAttributeFn(l,f.attributes[l],r);u!==null?f.attributes[l]=u:delete f.attributes[l]}if(l==="style"){if(!this.unattachedDoc)try{this.unattachedDoc=document.implementation.createHTMLDocument()}catch{this.unattachedDoc=this.doc}const u=this.unattachedDoc.createElement("span");t.oldValue&&u.setAttribute("style",t.oldValue);for(const m of Array.from(r.style)){const a=r.style.getPropertyValue(m),p=r.style.getPropertyPriority(m);a!==u.style.getPropertyValue(m)||p!==u.style.getPropertyPriority(m)?p===""?f.styleDiff[m]=a:f.styleDiff[m]=[a,p]:f._unchangedStyles[m]=[a,p]}for(const m of Array.from(u.style))r.style.getPropertyValue(m)===""&&(f.styleDiff[m]=!1)}else l==="open"&&r.tagName==="DIALOG"&&(r.matches("dialog:modal")?f.attributes.rr_open_mode="modal":f.attributes.rr_open_mode="non-modal")}break}case"childList":{if(re(t.target,this.blockClass,this.blockSelector,!0))return;if(t.target.tagName==="TEXTAREA"){this.genTextAreaValueMutation(t.target);return}t.addedNodes.forEach(r=>this.genAdds(r,t.target)),t.removedNodes.forEach(r=>{const l=this.mirror.getId(r),s=Te(t.target)?this.mirror.getId(L.host(t.target)):this.mirror.getId(t.target);re(t.target,this.blockClass,this.blockSelector,!1)||Rr(r,this.mirror,this.slimDOMOptions)||!zo(r,this.mirror)||(this.addedSet.has(r)?(Ar(this.addedSet,r),this.droppedSet.add(r)):this.addedSet.has(t.target)&&l===-1||Hi(t.target,this.mirror)||(this.movedSet.has(r)&&this.movedMap[ai(l,s)]?Ar(this.movedSet,r):(this.removes.push({parentId:s,id:l,isShadow:Te(t.target)&&Fe(t.target)?!0:void 0}),Jo(r,this.removesSubTreeCache))),this.mapRemoves.push(r))});break}}}),I(this,"genAdds",(t,r)=>{if(!this.processedNodeManager.inOtherBuffer(t,this)&&!(this.addedSet.has(t)||this.movedSet.has(t))){if(this.mirror.hasNode(t)){if(Rr(t,this.mirror,this.slimDOMOptions))return;this.movedSet.add(t);let l=null;r&&this.mirror.hasNode(r)&&(l=this.mirror.getId(r)),l&&l!==-1&&(this.movedMap[ai(this.mirror.getId(t),l)]=!0)}else this.addedSet.add(t),this.droppedSet.delete(t);re(t,this.blockClass,this.blockSelector,!1)||(L.childNodes(t).forEach(l=>this.genAdds(l)),Ir(t)&&L.childNodes(L.shadowRoot(t)).forEach(l=>{this.processedNodeManager.add(l,this),this.genAdds(l,t)}))}})}init(t){["mutationCb","blockClass","blockSelector","maskTextClass","maskTextSelector","inlineStylesheet","maskInputOptions","maskTextFn","maskInputFn","maskAttributeFn","keepIframeSrcFn","recordCanvas","inlineImages","slimDOMOptions","dataURLOptions","doc","mirror","iframeManager","stylesheetManager","shadowDomManager","canvasManager","processedNodeManager"].forEach(r=>{this[r]=t[r]})}freeze(){this.frozen=!0,this.canvasManager.freeze()}unfreeze(){this.frozen=!1,this.canvasManager.unfreeze(),this.emit()}isFrozen(){return this.frozen}lock(){this.locked=!0,this.canvasManager.lock()}unlock(){this.locked=!1,this.canvasManager.unlock(),this.emit()}reset(){this.shadowDomManager.reset(),this.canvasManager.reset()}}function Ar(e,t){e.delete(t),L.childNodes(t).forEach(r=>Ar(e,r))}function Jo(e,t){const r=[e];for(;r.length;){const l=r.pop();t.has(l)||(t.add(l),L.childNodes(l).forEach(s=>r.push(s)))}}function li(e,t,r){return e.size===0?!1:Yo(e,t)}function Yo(e,t,r){const l=L.parentNode(t);return l?e.has(l):!1}function ui(e,t){return e.size===0?!1:Xi(e,t)}function Xi(e,t){const r=L.parentNode(t);return r?e.has(r)?!0:Xi(e,r):!1}let Ue;function Qo(e){Ue=e}function Xo(){Ue=void 0}const T=e=>Ue?((...r)=>{try{return e(...r)}catch(l){if(Ue&&Ue(l)===!0)return;throw l}}):e;function ci(e){return(...t)=>{try{return e(...t)}catch(r){try{r._external_=!0}catch{}throw r}}}const ve=[];function qe(e){try{if("composedPath"in e){const t=e.composedPath();if(t.length)return t[0]}else if("path"in e&&e.path.length)return e.path[0]}catch{}return e&&e.target}function Ki(e,t){const r=new Vo;ve.push(r),r.init(e);const l=new(Bi())(T(r.processMutations.bind(r)));return l.observe(t,{attributes:!0,attributeOldValue:!0,characterData:!0,characterDataOldValue:!0,childList:!0,subtree:!0}),l}function Ko({mousemoveCb:e,sampling:t,doc:r,mirror:l}){if(t.mousemove===!1)return()=>{};const s=typeof t.mousemove=="number"?t.mousemove:50,f=typeof t.mousemoveCallback=="number"?t.mousemoveCallback:500;let u=[],m;const a=Be(T(c=>{const o=Date.now()-m;e(u.map(n=>(n.timeOffset-=o,n)),c),u=[],m=null}),f),p=T(Be(T(c=>{const o=qe(c),{clientX:n,clientY:d}=Er(c)?c.changedTouches[0]:c;m||(m=at()),u.push({x:n,y:d,id:l.getId(o),timeOffset:at()-m}),a(typeof DragEvent<"u"&&c instanceof DragEvent?D.Drag:c instanceof MouseEvent?D.MouseMove:D.TouchMove)}),s,{trailing:!1})),i=[te("mousemove",p,r),te("touchmove",p,r),te("drag",p,r)];return T(()=>{i.forEach(c=>c())})}function Zo({mouseInteractionCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){if(f.mouseInteraction===!1)return()=>{};const u=f.mouseInteraction===!0||f.mouseInteraction===void 0?{}:f.mouseInteraction,m=[];let a=null;const p=i=>c=>{const o=qe(c);if(re(o,l,s,!0))return;let n=null,d=i;if("pointerType"in c){switch(c.pointerType){case"mouse":n=ge.Mouse;break;case"touch":n=ge.Touch;break;case"pen":n=ge.Pen;break}n===ge.Touch?ie[i]===ie.MouseDown?d="TouchStart":ie[i]===ie.MouseUp&&(d="TouchEnd"):ge.Pen}else Er(c)&&(n=ge.Touch);n!==null?(a=n,(d.startsWith("Touch")&&n===ge.Touch||d.startsWith("Mouse")&&n===ge.Mouse)&&(n=null)):ie[i]===ie.Click&&(n=a,a=null);const h=Er(c)?c.changedTouches[0]:c;if(!h)return;const y=r.getId(o),{clientX:C,clientY:w}=h;T(e)({type:ie[d],id:y,x:C,y:w,...n!==null&&{pointerType:n}})};return Object.keys(ie).filter(i=>Number.isNaN(Number(i))&&!i.endsWith("_Departed")&&u[i]!==!1).forEach(i=>{let c=Ce(i);const o=p(i);if(window.PointerEvent)switch(ie[i]){case ie.MouseDown:case ie.MouseUp:c=c.replace("mouse","pointer");break;case ie.TouchStart:case ie.TouchEnd:return}m.push(te(c,o,t))}),T(()=>{m.forEach(i=>i())})}function Zi({scrollCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,sampling:f}){const u=T(Be(T(m=>{const a=qe(m);if(!a||re(a,l,s,!0))return;const p=r.getId(a);if(a===t&&t.defaultView){const i=zi(t.defaultView);e({id:p,x:i.left,y:i.top})}else e({id:p,x:a.scrollLeft,y:a.scrollTop})}),f.scroll||100));return te("scroll",u,t)}function ea({viewportResizeCb:e},{win:t}){let r=-1,l=-1;const s=T(Be(T(()=>{const f=Wi(),u=qi();(r!==f||l!==u)&&(e({width:Number(u),height:Number(f)}),r=f,l=u)}),200));return te("resize",s,t)}const ta=["INPUT","TEXTAREA","SELECT"],hi=new WeakMap;function ra({inputCb:e,doc:t,mirror:r,blockClass:l,blockSelector:s,ignoreClass:f,ignoreSelector:u,maskInputOptions:m,maskInputFn:a,sampling:p,userTriggeredOnInput:i}){function c(w){let S=qe(w);const b=w.isTrusted,g=S&&S.tagName;if(S&&g==="OPTION"&&(S=L.parentElement(S)),!S||!g||ta.indexOf(g)<0||re(S,l,s,!0)||S.classList.contains(f)||u&&S.matches(u))return;let v=S.value,x=!1;const O=it(S)||"";O==="radio"||O==="checkbox"?x=S.checked:(m[g.toLowerCase()]||m[O])&&(v=st({element:S,maskInputOptions:m,tagName:g,type:O,value:v,maskInputFn:a})),o(S,i?{text:v,isChecked:x,userTriggered:b}:{text:v,isChecked:x});const A=S.name;O==="radio"&&A&&x&&t.querySelectorAll(`input[type="radio"][name="${A}"]`).forEach(M=>{if(M!==S){const $=M.value;o(M,i?{text:$,isChecked:!x,userTriggered:!1}:{text:$,isChecked:!x})}})}function o(w,S){const b=hi.get(w);if(!b||b.text!==S.text||b.isChecked!==S.isChecked){hi.set(w,S);const g=r.getId(w);T(e)({...S,id:g})}}const d=(p.input==="last"?["change"]:["input","change"]).map(w=>te(w,T(c),t)),h=t.defaultView;if(!h)return()=>{d.forEach(w=>w())};const y=h.Object.getOwnPropertyDescriptor(h.HTMLInputElement.prototype,"value"),C=[[h.HTMLInputElement.prototype,"value"],[h.HTMLInputElement.prototype,"checked"],[h.HTMLSelectElement.prototype,"value"],[h.HTMLTextAreaElement.prototype,"value"],[h.HTMLSelectElement.prototype,"selectedIndex"],[h.HTMLOptionElement.prototype,"selected"]];return y&&y.set&&d.push(...C.map(w=>St(w[0],w[1],{set(){T(c)({target:this,isTrusted:!1})}},!1,h))),T(()=>{d.forEach(w=>w())})}function lt(e){const t=[];function r(l,s){if(Xe("CSSGroupingRule")&&l.parentRule instanceof CSSGroupingRule||Xe("CSSMediaRule")&&l.parentRule instanceof CSSMediaRule||Xe("CSSSupportsRule")&&l.parentRule instanceof CSSSupportsRule||Xe("CSSConditionRule")&&l.parentRule instanceof CSSConditionRule){const u=Array.from(l.parentRule.cssRules).indexOf(l);s.unshift(u)}else if(l.parentStyleSheet){const u=Array.from(l.parentStyleSheet.cssRules).indexOf(l);s.unshift(u)}return s}return r(e,t)}function we(e,t,r){let l,s;return e?(e.ownerNode?l=t.getId(e.ownerNode):s=r.getId(e),{styleId:s,id:l}):{}}function sa({styleSheetRuleCb:e,mirror:t,stylesheetManager:r},{win:l}){if(!l.CSSStyleSheet||!l.CSSStyleSheet.prototype)return()=>{};const s=l.CSSStyleSheet.prototype.insertRule;l.CSSStyleSheet.prototype.insertRule=new Proxy(s,{apply:T((i,c,o)=>{const[n,d]=o,{id:h,styleId:y}=we(c,t,r.styleMirror);return(h&&h!==-1||y&&y!==-1)&&e({id:h,styleId:y,adds:[{rule:n,index:d}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.addRule=function(i,c,o=this.cssRules.length){const n=`${i} { ${c} }`;return l.CSSStyleSheet.prototype.insertRule.apply(this,[n,o])};const f=l.CSSStyleSheet.prototype.deleteRule;l.CSSStyleSheet.prototype.deleteRule=new Proxy(f,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,removes:[{index:n}]}),ci(()=>i.apply(c,o))()})}),l.CSSStyleSheet.prototype.removeRule=function(i){return l.CSSStyleSheet.prototype.deleteRule.apply(this,[i])};let u;l.CSSStyleSheet.prototype.replace&&(u=l.CSSStyleSheet.prototype.replace,l.CSSStyleSheet.prototype.replace=new Proxy(u,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replace:n}),i.apply(c,o)})}));let m;l.CSSStyleSheet.prototype.replaceSync&&(m=l.CSSStyleSheet.prototype.replaceSync,l.CSSStyleSheet.prototype.replaceSync=new Proxy(m,{apply:T((i,c,o)=>{const[n]=o,{id:d,styleId:h}=we(c,t,r.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,replaceSync:n}),i.apply(c,o)})}));const a={};Ke("CSSGroupingRule")?a.CSSGroupingRule=l.CSSGroupingRule:(Ke("CSSMediaRule")&&(a.CSSMediaRule=l.CSSMediaRule),Ke("CSSConditionRule")&&(a.CSSConditionRule=l.CSSConditionRule),Ke("CSSSupportsRule")&&(a.CSSSupportsRule=l.CSSSupportsRule));const p={};return Object.entries(a).forEach(([i,c])=>{p[i]={insertRule:c.prototype.insertRule,deleteRule:c.prototype.deleteRule},c.prototype.insertRule=new Proxy(p[i].insertRule,{apply:T((o,n,d)=>{const[h,y]=d,{id:C,styleId:w}=we(n.parentStyleSheet,t,r.styleMirror);return(C&&C!==-1||w&&w!==-1)&&e({id:C,styleId:w,adds:[{rule:h,index:[...lt(n),y||0]}]}),o.apply(n,d)})}),c.prototype.deleteRule=new Proxy(p[i].deleteRule,{apply:T((o,n,d)=>{const[h]=d,{id:y,styleId:C}=we(n.parentStyleSheet,t,r.styleMirror);return(y&&y!==-1||C&&C!==-1)&&e({id:y,styleId:C,removes:[{index:[...lt(n),h]}]}),o.apply(n,d)})})}),T(()=>{l.CSSStyleSheet.prototype.insertRule=s,l.CSSStyleSheet.prototype.deleteRule=f,u&&(l.CSSStyleSheet.prototype.replace=u),m&&(l.CSSStyleSheet.prototype.replaceSync=m),Object.entries(a).forEach(([i,c])=>{c.prototype.insertRule=p[i].insertRule,c.prototype.deleteRule=p[i].deleteRule})})}function en({mirror:e,stylesheetManager:t},r){var l,s,f;let u=null;r.nodeName==="#document"?u=e.getId(r):u=e.getId(L.host(r));const m=r.nodeName==="#document"?(l=r.defaultView)==null?void 0:l.Document:(f=(s=r.ownerDocument)==null?void 0:s.defaultView)==null?void 0:f.ShadowRoot,a=m?.prototype?Object.getOwnPropertyDescriptor(m?.prototype,"adoptedStyleSheets"):void 0;return u===null||u===-1||!m||!a?()=>{}:(Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get(){var p;return(p=a.get)==null?void 0:p.call(this)},set(p){var i;const c=(i=a.set)==null?void 0:i.call(this,p);if(u!==null&&u!==-1)try{t.adoptStyleSheets(p,u)}catch{}return c}}),T(()=>{Object.defineProperty(r,"adoptedStyleSheets",{configurable:a.configurable,enumerable:a.enumerable,get:a.get,set:a.set})}))}function ia({styleDeclarationCb:e,mirror:t,ignoreCSSAttributes:r,stylesheetManager:l},{win:s}){const f=s.CSSStyleDeclaration.prototype.setProperty;s.CSSStyleDeclaration.prototype.setProperty=new Proxy(f,{apply:T((m,a,p)=>{var i;const[c,o,n]=p;if(r.has(c))return f.apply(a,[c,o,n]);const{id:d,styleId:h}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(d&&d!==-1||h&&h!==-1)&&e({id:d,styleId:h,set:{property:c,value:o,priority:n},index:lt(a.parentRule)}),m.apply(a,p)})});const u=s.CSSStyleDeclaration.prototype.removeProperty;return s.CSSStyleDeclaration.prototype.removeProperty=new Proxy(u,{apply:T((m,a,p)=>{var i;const[c]=p;if(r.has(c))return u.apply(a,[c]);const{id:o,styleId:n}=we((i=a.parentRule)==null?void 0:i.parentStyleSheet,t,l.styleMirror);return(o&&o!==-1||n&&n!==-1)&&e({id:o,styleId:n,remove:{property:c},index:lt(a.parentRule)}),m.apply(a,p)})}),T(()=>{s.CSSStyleDeclaration.prototype.setProperty=f,s.CSSStyleDeclaration.prototype.removeProperty=u})}function na({mediaInteractionCb:e,blockClass:t,blockSelector:r,mirror:l,sampling:s,doc:f}){const u=T(a=>Be(T(p=>{const i=qe(p);if(!i||re(i,t,r,!0))return;const{currentTime:c,volume:o,muted:n,playbackRate:d,loop:h}=i;e({type:a,id:l.getId(i),currentTime:c,volume:o,muted:n,playbackRate:d,loop:h})}),s.media||500)),m=[te("play",u(Ae.Play),f),te("pause",u(Ae.Pause),f),te("seeked",u(Ae.Seeked),f),te("volumechange",u(Ae.VolumeChange),f),te("ratechange",u(Ae.RateChange),f)];return T(()=>{m.forEach(a=>a())})}function oa({fontCb:e,doc:t}){const r=t.defaultView;if(!r)return()=>{};const l=[],s=new WeakMap,f=r.FontFace;r.FontFace=function(a,p,i){const c=new f(a,p,i);return s.set(c,{family:a,buffer:typeof p!="string",descriptors:i,fontSource:typeof p=="string"?p:JSON.stringify(Array.from(new Uint8Array(p)))}),c};const u=Le(t.fonts,"add",function(m){return function(a){return setTimeout(T(()=>{const p=s.get(a);p&&(e(p),s.delete(a))}),0),m.apply(this,[a])}});return l.push(()=>{r.FontFace=f}),l.push(u),T(()=>{l.forEach(m=>m())})}function aa(e){const{doc:t,mirror:r,blockClass:l,blockSelector:s,selectionCb:f}=e;let u=!0;const m=T(()=>{const a=t.getSelection();if(!a||u&&a?.isCollapsed)return;u=a.isCollapsed||!1;const p=[],i=a.rangeCount||0;for(let c=0;c{}:Le(r.customElements,"define",function(s){return function(f,u,m){try{t({define:{name:f}})}catch{console.warn(`Custom element callback failed for ${f}`)}return s.apply(this,[f,u,m])}})}function ua(e,t){const{mutationCb:r,mousemoveCb:l,mouseInteractionCb:s,scrollCb:f,viewportResizeCb:u,inputCb:m,mediaInteractionCb:a,styleSheetRuleCb:p,styleDeclarationCb:i,canvasMutationCb:c,fontCb:o,selectionCb:n,customElementCb:d}=e;e.mutationCb=(...h)=>{t.mutation&&t.mutation(...h),r(...h)},e.mousemoveCb=(...h)=>{t.mousemove&&t.mousemove(...h),l(...h)},e.mouseInteractionCb=(...h)=>{t.mouseInteraction&&t.mouseInteraction(...h),s(...h)},e.scrollCb=(...h)=>{t.scroll&&t.scroll(...h),f(...h)},e.viewportResizeCb=(...h)=>{t.viewportResize&&t.viewportResize(...h),u(...h)},e.inputCb=(...h)=>{t.input&&t.input(...h),m(...h)},e.mediaInteractionCb=(...h)=>{t.mediaInteaction&&t.mediaInteaction(...h),a(...h)},e.styleSheetRuleCb=(...h)=>{t.styleSheetRule&&t.styleSheetRule(...h),p(...h)},e.styleDeclarationCb=(...h)=>{t.styleDeclaration&&t.styleDeclaration(...h),i(...h)},e.canvasMutationCb=(...h)=>{t.canvasMutation&&t.canvasMutation(...h),c(...h)},e.fontCb=(...h)=>{t.font&&t.font(...h),o(...h)},e.selectionCb=(...h)=>{t.selection&&t.selection(...h),n(...h)},e.customElementCb=(...h)=>{t.customElement&&t.customElement(...h),d(...h)}}function ca(e,t={}){const r=e.doc.defaultView;if(!r)return()=>{};ua(e,t);let l;e.recordDOM&&(l=Ki(e,e.doc));const s=Ko(e),f=Zo(e),u=Zi(e),m=ea(e,{win:r}),a=ra(e),p=na(e);let i=()=>{},c=()=>{},o=()=>{},n=()=>{};e.recordDOM&&(i=sa(e,{win:r}),c=en(e,e.doc),o=ia(e,{win:r}),e.collectFonts&&(n=oa(e)));const d=aa(e),h=la(e),y=[];for(const C of e.plugins)y.push(C.observer(C.callback,r,C.options));return T(()=>{ve.forEach(C=>C.reset()),l?.disconnect(),s(),f(),u(),m(),a(),p(),i(),c(),o(),n(),d(),h(),y.forEach(C=>C())})}function Xe(e){return typeof window[e]<"u"}function Ke(e){return!!(typeof window[e]<"u"&&window[e].prototype&&"insertRule"in window[e].prototype&&"deleteRule"in window[e].prototype)}class fi{constructor(t){I(this,"iframeIdToRemoteIdMap",new WeakMap),I(this,"iframeRemoteIdToIdMap",new WeakMap),this.generateIdFn=t}getId(t,r,l,s){const f=l||this.getIdToRemoteIdMap(t),u=s||this.getRemoteIdToIdMap(t);let m=f.get(r);return m||(m=this.generateIdFn(),f.set(r,m),u.set(m,r)),m}getIds(t,r){const l=this.getIdToRemoteIdMap(t),s=this.getRemoteIdToIdMap(t);return r.map(f=>this.getId(t,f,l,s))}getRemoteId(t,r,l){const s=l||this.getRemoteIdToIdMap(t);if(typeof r!="number")return r;const f=s.get(r);return f||-1}getRemoteIds(t,r){const l=this.getRemoteIdToIdMap(t);return r.map(s=>this.getRemoteId(t,s,l))}reset(t){if(!t){this.iframeIdToRemoteIdMap=new WeakMap,this.iframeRemoteIdToIdMap=new WeakMap;return}this.iframeIdToRemoteIdMap.delete(t),this.iframeRemoteIdToIdMap.delete(t)}getIdToRemoteIdMap(t){let r=this.iframeIdToRemoteIdMap.get(t);return r||(r=new Map,this.iframeIdToRemoteIdMap.set(t,r)),r}getRemoteIdToIdMap(t){let r=this.iframeRemoteIdToIdMap.get(t);return r||(r=new Map,this.iframeRemoteIdToIdMap.set(t,r)),r}}class ha{constructor(t){I(this,"iframes",new WeakMap),I(this,"crossOriginIframeMap",new WeakMap),I(this,"crossOriginIframeMirror",new fi(xi)),I(this,"crossOriginIframeStyleMirror"),I(this,"crossOriginIframeRootIdMap",new WeakMap),I(this,"mirror"),I(this,"mutationCb"),I(this,"wrappedEmit"),I(this,"loadListener"),I(this,"stylesheetManager"),I(this,"recordCrossOriginIframes"),this.mutationCb=t.mutationCb,this.wrappedEmit=t.wrappedEmit,this.stylesheetManager=t.stylesheetManager,this.recordCrossOriginIframes=t.recordCrossOriginIframes,this.crossOriginIframeStyleMirror=new fi(this.stylesheetManager.styleMirror.generateId.bind(this.stylesheetManager.styleMirror)),this.mirror=t.mirror,this.recordCrossOriginIframes&&window.addEventListener("message",this.handleMessage.bind(this))}addIframe(t){this.iframes.set(t,!0),t.contentWindow&&this.crossOriginIframeMap.set(t.contentWindow,t)}addLoadListener(t){this.loadListener=t}attachIframe(t,r){var l,s;this.mutationCb({adds:[{parentId:this.mirror.getId(t),nextId:null,node:r}],removes:[],texts:[],attributes:[],isAttachIframe:!0}),this.recordCrossOriginIframes&&((l=t.contentWindow)==null||l.addEventListener("message",this.handleMessage.bind(this))),(s=this.loadListener)==null||s.call(this,t),t.contentDocument&&t.contentDocument.adoptedStyleSheets&&t.contentDocument.adoptedStyleSheets.length>0&&this.stylesheetManager.adoptStyleSheets(t.contentDocument.adoptedStyleSheets,this.mirror.getId(t.contentDocument))}handleMessage(t){const r=t;if(r.data.type!=="rrweb"||r.origin!==r.data.origin||!t.source)return;const s=this.crossOriginIframeMap.get(t.source);if(!s)return;const f=this.transformCrossOriginEvent(s,r.data.event);f&&this.wrappedEmit(f,r.data.isCheckout)}transformCrossOriginEvent(t,r){var l;switch(r.type){case U.FullSnapshot:{this.crossOriginIframeMirror.reset(t),this.crossOriginIframeStyleMirror.reset(t),this.replaceIdOnNode(r.data.node,t);const s=r.data.node.id;return this.crossOriginIframeRootIdMap.set(t,s),this.patchRootIdOnNode(r.data.node,s),{timestamp:r.timestamp,type:U.IncrementalSnapshot,data:{source:D.Mutation,adds:[{parentId:this.mirror.getId(t),nextId:null,node:r.data.node}],removes:[],texts:[],attributes:[],isAttachIframe:!0}}}case U.Meta:case U.Load:case U.DomContentLoaded:return!1;case U.Plugin:return r;case U.Custom:return this.replaceIds(r.data.payload,t,["id","parentId","previousId","nextId"]),r;case U.IncrementalSnapshot:switch(r.data.source){case D.Mutation:return r.data.adds.forEach(s=>{this.replaceIds(s,t,["parentId","nextId","previousId"]),this.replaceIdOnNode(s.node,t);const f=this.crossOriginIframeRootIdMap.get(t);f&&this.patchRootIdOnNode(s.node,f)}),r.data.removes.forEach(s=>{this.replaceIds(s,t,["parentId","id"])}),r.data.attributes.forEach(s=>{this.replaceIds(s,t,["id"])}),r.data.texts.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.Drag:case D.TouchMove:case D.MouseMove:return r.data.positions.forEach(s=>{this.replaceIds(s,t,["id"])}),r;case D.ViewportResize:return!1;case D.MediaInteraction:case D.MouseInteraction:case D.Scroll:case D.CanvasMutation:case D.Input:return this.replaceIds(r.data,t,["id"]),r;case D.StyleSheetRule:case D.StyleDeclaration:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleId"]),r;case D.Font:return r;case D.Selection:return r.data.ranges.forEach(s=>{this.replaceIds(s,t,["start","end"])}),r;case D.AdoptedStyleSheet:return this.replaceIds(r.data,t,["id"]),this.replaceStyleIds(r.data,t,["styleIds"]),(l=r.data.styles)==null||l.forEach(s=>{this.replaceStyleIds(s,t,["styleId"])}),r}}return!1}replace(t,r,l,s){for(const f of s)!Array.isArray(r[f])&&typeof r[f]!="number"||(Array.isArray(r[f])?r[f]=t.getIds(l,r[f]):r[f]=t.getId(l,r[f]));return r}replaceIds(t,r,l){return this.replace(this.crossOriginIframeMirror,t,r,l)}replaceStyleIds(t,r,l){return this.replace(this.crossOriginIframeStyleMirror,t,r,l)}replaceIdOnNode(t,r){this.replaceIds(t,r,["id","rootId"]),"childNodes"in t&&t.childNodes.forEach(l=>{this.replaceIdOnNode(l,r)})}patchRootIdOnNode(t,r){t.type!==Qi.Document&&!t.rootId&&(t.rootId=r),"childNodes"in t&&t.childNodes.forEach(l=>{this.patchRootIdOnNode(l,r)})}}class fa{constructor(t){I(this,"shadowDoms",new WeakSet),I(this,"mutationCb"),I(this,"scrollCb"),I(this,"bypassOptions"),I(this,"mirror"),I(this,"restoreHandlers",[]),this.mutationCb=t.mutationCb,this.scrollCb=t.scrollCb,this.bypassOptions=t.bypassOptions,this.mirror=t.mirror,this.init()}init(){this.reset(),this.patchAttachShadow(Element,document)}addShadowRoot(t,r){if(!Fe(t)||this.shadowDoms.has(t))return;this.shadowDoms.add(t);const l=Ki({...this.bypassOptions,doc:r,mutationCb:this.mutationCb,mirror:this.mirror,shadowDomManager:this},t);this.restoreHandlers.push(()=>l.disconnect()),this.restoreHandlers.push(Zi({...this.bypassOptions,scrollCb:this.scrollCb,doc:t,mirror:this.mirror})),setTimeout(()=>{t.adoptedStyleSheets&&t.adoptedStyleSheets.length>0&&this.bypassOptions.stylesheetManager.adoptStyleSheets(t.adoptedStyleSheets,this.mirror.getId(L.host(t))),this.restoreHandlers.push(en({mirror:this.mirror,stylesheetManager:this.bypassOptions.stylesheetManager},t))},0)}observeAttachShadow(t){!t.contentWindow||!t.contentDocument||this.patchAttachShadow(t.contentWindow.Element,t.contentDocument)}patchAttachShadow(t,r){const l=this;this.restoreHandlers.push(Le(t.prototype,"attachShadow",function(s){return function(f){const u=s.call(this,f),m=L.shadowRoot(this);return m&&Yi(this)&&l.addShadowRoot(m,r),u}}))}reset(){this.restoreHandlers.forEach(t=>{try{t()}catch{}}),this.restoreHandlers=[],this.shadowDoms=new WeakSet}}var Pe="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",pa=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var Ze=0;Ze>2],s+=Pe[(t[r]&3)<<4|t[r+1]>>4],s+=Pe[(t[r+1]&15)<<2|t[r+2]>>6],s+=Pe[t[r+2]&63];return l%3===2?s=s.substring(0,s.length-1)+"=":l%3===1&&(s=s.substring(0,s.length-2)+"=="),s};const pi=new Map;function ma(e,t){let r=pi.get(e);return r||(r=new Map,pi.set(e,r)),r.has(t)||r.set(t,[]),r.get(t)}const tn=(e,t,r)=>{if(!e||!(sn(e,t)||typeof e=="object"))return;const l=e.constructor.name,s=ma(r,l);let f=s.indexOf(e);return f===-1&&(f=s.length,s.push(e)),f};function et(e,t,r){if(e instanceof Array)return e.map(l=>et(l,t,r));if(e===null)return e;if(e instanceof Float32Array||e instanceof Float64Array||e instanceof Int32Array||e instanceof Uint32Array||e instanceof Uint8Array||e instanceof Uint16Array||e instanceof Int16Array||e instanceof Int8Array||e instanceof Uint8ClampedArray)return{rr_type:e.constructor.name,args:[Object.values(e)]};if(e instanceof ArrayBuffer){const l=e.constructor.name,s=da(e);return{rr_type:l,base64:s}}else{if(e instanceof DataView)return{rr_type:e.constructor.name,args:[et(e.buffer,t,r),e.byteOffset,e.byteLength]};if(e instanceof HTMLImageElement){const l=e.constructor.name,{src:s}=e;return{rr_type:l,src:s}}else if(e instanceof HTMLCanvasElement){const l="HTMLImageElement",s=e.toDataURL();return{rr_type:l,src:s}}else{if(e instanceof ImageData)return{rr_type:e.constructor.name,args:[et(e.data,t,r),e.width,e.height]};if(sn(e,t)||typeof e=="object"){const l=e.constructor.name,s=tn(e,t,r);return{rr_type:l,index:s}}}}return e}const rn=(e,t,r)=>e.map(l=>et(l,t,r)),sn=(e,t)=>!!["WebGLActiveInfo","WebGLBuffer","WebGLFramebuffer","WebGLProgram","WebGLRenderbuffer","WebGLShader","WebGLShaderPrecisionFormat","WebGLTexture","WebGLUniformLocation","WebGLVertexArrayObject","WebGLVertexArrayObjectOES"].filter(s=>typeof t[s]=="function").find(s=>e instanceof t[s]);function ga(e,t,r,l){const s=[],f=Object.getOwnPropertyNames(t.CanvasRenderingContext2D.prototype);for(const u of f)try{if(typeof t.CanvasRenderingContext2D.prototype[u]!="function")continue;const m=Le(t.CanvasRenderingContext2D.prototype,u,function(a){return function(...p){return re(this.canvas,r,l,!0)||setTimeout(()=>{const i=rn(p,t,this);e(this.canvas,{type:_e["2D"],property:u,args:i})},0),a.apply(this,p)}});s.push(m)}catch{const m=St(t.CanvasRenderingContext2D.prototype,u,{set(a){e(this.canvas,{type:_e["2D"],property:u,args:[a],setter:!0})}});s.push(m)}return()=>{s.forEach(u=>u())}}function ya(e){return e==="experimental-webgl"?"webgl":e}function di(e,t,r,l){const s=[];try{const f=Le(e.HTMLCanvasElement.prototype,"getContext",function(u){return function(m,...a){if(!re(this,t,r,!0)){const p=ya(m);if("__context"in this||(this.__context=p),l&&["webgl","webgl2"].includes(p))if(a[0]&&typeof a[0]=="object"){const i=a[0];i.preserveDrawingBuffer||(i.preserveDrawingBuffer=!0)}else a.splice(0,1,{preserveDrawingBuffer:!0})}return u.apply(this,[m,...a])}});s.push(f)}catch{console.error("failed to patch HTMLCanvasElement.prototype.getContext")}return()=>{s.forEach(f=>f())}}function mi(e,t,r,l,s,f){const u=[],m=Object.getOwnPropertyNames(e);for(const a of m)if(!["isContextLost","canvas","drawingBufferWidth","drawingBufferHeight"].includes(a))try{if(typeof e[a]!="function")continue;const p=Le(e,a,function(i){return function(...c){const o=i.apply(this,c);if(tn(o,f,this),"tagName"in this.canvas&&!re(this.canvas,l,s,!0)){const n=rn(c,f,this),d={type:t,property:a,args:n};r(this.canvas,d)}return o}});u.push(p)}catch{const p=St(e,a,{set(i){r(this.canvas,{type:t,property:a,args:[i],setter:!0})}});u.push(p)}return u}function wa(e,t,r,l){const s=[];return s.push(...mi(t.WebGLRenderingContext.prototype,_e.WebGL,e,r,l,t)),typeof t.WebGL2RenderingContext<"u"&&s.push(...mi(t.WebGL2RenderingContext.prototype,_e.WebGL2,e,r,l,t)),()=>{s.forEach(f=>f())}}const nn=`(function() { - "use strict"; - var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var lookup = typeof Uint8Array === "undefined" ? [] : new Uint8Array(256); - for (var i = 0; i < chars.length; i++) { - lookup[chars.charCodeAt(i)] = i; - } - var encode = function(arraybuffer) { - var bytes = new Uint8Array(arraybuffer), i2, len = bytes.length, base64 = ""; - for (i2 = 0; i2 < len; i2 += 3) { - base64 += chars[bytes[i2] >> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`,gi=typeof self<"u"&&self.Blob&&new Blob([nn],{type:"text/javascript;charset=utf-8"});function ba(e){let t;try{if(t=gi&&(self.URL||self.webkitURL).createObjectURL(gi),!t)throw"";const r=new Worker(t,{name:e?.name});return r.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(t)}),r}catch{return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(nn),{name:e?.name})}finally{t&&(self.URL||self.webkitURL).revokeObjectURL(t)}}class Sa{constructor(t){I(this,"pendingCanvasMutations",new Map),I(this,"rafStamps",{latestId:0,invokeId:null}),I(this,"mirror"),I(this,"mutationCb"),I(this,"resetObservers"),I(this,"frozen",!1),I(this,"locked",!1),I(this,"processMutation",(a,p)=>{(this.rafStamps.invokeId&&this.rafStamps.latestId!==this.rafStamps.invokeId||!this.rafStamps.invokeId)&&(this.rafStamps.invokeId=this.rafStamps.latestId),this.pendingCanvasMutations.has(a)||this.pendingCanvasMutations.set(a,[]),this.pendingCanvasMutations.get(a).push(p)});const{sampling:r="all",win:l,blockClass:s,blockSelector:f,recordCanvas:u,dataURLOptions:m}=t;this.mutationCb=t.mutationCb,this.mirror=t.mirror,u&&r==="all"&&this.initCanvasMutationObserver(l,s,f),u&&typeof r=="number"&&this.initCanvasFPSObserver(r,l,s,f,{dataURLOptions:m})}reset(){this.pendingCanvasMutations.clear(),this.resetObservers&&this.resetObservers()}freeze(){this.frozen=!0}unfreeze(){this.frozen=!1}lock(){this.locked=!0}unlock(){this.locked=!1}initCanvasFPSObserver(t,r,l,s,f){const u=di(r,l,s,!0),m=new Map,a=new ba;a.onmessage=d=>{const{id:h}=d.data;if(m.set(h,!1),!("base64"in d.data))return;const{base64:y,type:C,width:w,height:S}=d.data;this.mutationCb({id:h,type:_e["2D"],commands:[{property:"clearRect",args:[0,0,w,S]},{property:"drawImage",args:[{rr_type:"ImageBitmap",args:[{rr_type:"Blob",data:[{rr_type:"ArrayBuffer",base64:y}],type:C}]},0,0]}]})};const p=1e3/t;let i=0,c;const o=()=>{const d=[];return r.document.querySelectorAll("canvas").forEach(h=>{re(h,l,s,!0)||d.push(h)}),d},n=d=>{if(i&&d-i{var y;const C=this.mirror.getId(h);if(m.get(C)||h.width===0||h.height===0)return;if(m.set(C,!0),["webgl","webgl2"].includes(h.__context)){const S=h.getContext(h.__context);((y=S?.getContextAttributes())==null?void 0:y.preserveDrawingBuffer)===!1&&S.clear(S.COLOR_BUFFER_BIT)}const w=await createImageBitmap(h);a.postMessage({id:C,bitmap:w,width:h.width,height:h.height,dataURLOptions:f.dataURLOptions},[w])}),c=requestAnimationFrame(n)};c=requestAnimationFrame(n),this.resetObservers=()=>{u(),cancelAnimationFrame(c)}}initCanvasMutationObserver(t,r,l){this.startRAFTimestamping(),this.startPendingCanvasMutationFlusher();const s=di(t,r,l,!1),f=ga(this.processMutation.bind(this),t,r,l),u=wa(this.processMutation.bind(this),t,r,l);this.resetObservers=()=>{s(),f(),u()}}startPendingCanvasMutationFlusher(){requestAnimationFrame(()=>this.flushPendingCanvasMutations())}startRAFTimestamping(){const t=r=>{this.rafStamps.latestId=r,requestAnimationFrame(t)};requestAnimationFrame(t)}flushPendingCanvasMutations(){this.pendingCanvasMutations.forEach((t,r)=>{const l=this.mirror.getId(r);this.flushPendingCanvasMutationFor(r,l)}),requestAnimationFrame(()=>this.flushPendingCanvasMutations())}flushPendingCanvasMutationFor(t,r){if(this.frozen||this.locked)return;const l=this.pendingCanvasMutations.get(t);if(!l||r===-1)return;const s=l.map(u=>{const{type:m,...a}=u;return a}),{type:f}=l[0];this.mutationCb({id:r,type:f,commands:s}),this.pendingCanvasMutations.delete(t)}}class va{constructor(t){I(this,"trackedLinkElements",new WeakSet),I(this,"mutationCb"),I(this,"adoptedStyleSheetCb"),I(this,"styleMirror",new qo),this.mutationCb=t.mutationCb,this.adoptedStyleSheetCb=t.adoptedStyleSheetCb}attachLinkElement(t,r){"_cssText"in r.attributes&&this.mutationCb({adds:[],removes:[],texts:[],attributes:[{id:r.id,attributes:r.attributes}]}),this.trackLinkElement(t)}trackLinkElement(t){this.trackedLinkElements.has(t)||(this.trackedLinkElements.add(t),this.trackStylesheetInLinkElement(t))}adoptStyleSheets(t,r){if(t.length===0)return;const l={id:r,styleIds:[]},s=[];for(const f of t){let u;this.styleMirror.has(f)?u=this.styleMirror.getId(f):(u=this.styleMirror.add(f),s.push({styleId:u,rules:Array.from(f.rules||CSSRule,(m,a)=>({rule:Si(m,f.href),index:a}))})),l.styleIds.push(u)}s.length>0&&(l.styles=s),this.adoptedStyleSheetCb(l)}reset(){this.styleMirror.reset(),this.trackedLinkElements=new WeakSet}trackStylesheetInLinkElement(t){}}class Ca{constructor(){I(this,"nodeMap",new WeakMap),I(this,"active",!1)}inOtherBuffer(t,r){const l=this.nodeMap.get(t);return l&&Array.from(l).some(s=>s!==r)}add(t,r){this.active||(this.active=!0,requestAnimationFrame(()=>{this.nodeMap=new WeakMap,this.active=!1})),this.nodeMap.set(t,(this.nodeMap.get(t)||new Set).add(r))}destroy(){}}let J,tt,Or,ut=!1;try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((Jr=e.contentWindow)==null?void 0:Jr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}const ce=In();function Oe(e={}){const{emit:t,checkoutEveryNms:r,checkoutEveryNth:l,blockClass:s="rr-block",blockSelector:f=null,ignoreClass:u="rr-ignore",ignoreSelector:m=null,maskTextClass:a="rr-mask",maskTextSelector:p=null,inlineStylesheet:i=!0,maskAllInputs:c,maskInputOptions:o,slimDOMOptions:n,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,hooks:C,packFn:w,sampling:S={},dataURLOptions:b={},mousemoveWait:g,recordDOM:v=!0,recordCanvas:x=!1,recordCrossOriginIframes:O=!1,recordAfter:A=e.recordAfter==="DOMContentLoaded"?e.recordAfter:"load",userTriggeredOnInput:M=!1,collectFonts:$=!1,inlineImages:k=!1,plugins:R,keepIframeSrcFn:Z=()=>!1,ignoreCSSAttributes:N=new Set([]),errorHandler:ee,applyBackgroundColorToBlockedElements:H=!1}=e;Qo(ee);const F=O?window.parent===window:!0;let Q=!1;if(!F)try{window.parent.document&&(Q=!1)}catch{Q=!0}if(F&&!t)throw new Error("emit function is required");if(!F&&!Q)return()=>{};g!==void 0&&S.mousemove===void 0&&(S.mousemove=g),ce.reset();const W=c===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:o!==void 0?o:{password:!0},P=n===!0||n==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaVerification:!0,headMetaAuthorship:n==="all",headMetaDescKeywords:n==="all",headTitleMutations:n==="all"}:n||{};Wo();let ye,E=0;const pe=_=>{for(const ue of R||[])ue.eventProcessor&&(_=ue.eventProcessor(_));return w&&!Q&&(_=w(_)),_};J=(_,ue)=>{var G;const V=_;if(V.timestamp=at(),(G=ve[0])!=null&&G.isFrozen()&&V.type!==U.FullSnapshot&&!(V.type===U.IncrementalSnapshot&&V.data.source===D.Mutation)&&ve.forEach(me=>me.unfreeze()),F)t?.(pe(V),ue);else if(Q){const me={type:"rrweb",event:pe(V),origin:window.location.origin,isCheckout:ue};window.parent.postMessage(me,"*")}if(V.type===U.FullSnapshot)ye=V,E=0;else if(V.type===U.IncrementalSnapshot){if(V.data.source===D.Mutation&&V.data.isAttachIframe)return;E++;const me=l&&E>=l,B=r&&V.timestamp-ye.timestamp>r;(me||B)&&tt(!0)}};const se=_=>{J({type:U.IncrementalSnapshot,data:{source:D.Mutation,..._}})},Me=_=>J({type:U.IncrementalSnapshot,data:{source:D.Scroll,..._}}),de=_=>J({type:U.IncrementalSnapshot,data:{source:D.CanvasMutation,..._}}),De=_=>J({type:U.IncrementalSnapshot,data:{source:D.AdoptedStyleSheet,..._}}),ae=new va({mutationCb:se,adoptedStyleSheetCb:De}),le=new ha({mirror:ce,mutationCb:se,stylesheetManager:ae,recordCrossOriginIframes:O,wrappedEmit:J});for(const _ of R||[])_.getMirror&&_.getMirror({nodeMirror:ce,crossOriginIframeMirror:le.crossOriginIframeMirror,crossOriginIframeStyleMirror:le.crossOriginIframeStyleMirror});const X=new Ca;Or=new Sa({recordCanvas:x,mutationCb:de,win:window,blockClass:s,blockSelector:f,mirror:ce,sampling:S.canvas,dataURLOptions:b});const ne=new fa({mutationCb:se,scrollCb:Me,bypassOptions:{blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskInputOptions:W,dataURLOptions:b,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,recordCanvas:x,inlineImages:k,sampling:S,slimDOMOptions:P,iframeManager:le,stylesheetManager:ae,canvasManager:Or,keepIframeSrcFn:Z,processedNodeManager:X},mirror:ce});tt=(_=!1)=>{if(!v)return;J({type:U.Meta,data:{href:window.location.href,width:qi(),height:Wi()}},_),ae.reset(),ne.init(),ve.forEach(G=>G.lock());const ue=Zn(document,{mirror:ce,blockClass:s,blockSelector:f,maskTextClass:a,maskTextSelector:p,inlineStylesheet:i,maskAllInputs:W,maskTextFn:h,maskInputFn:d,maskAttributeFn:y,slimDOM:P,dataURLOptions:b,recordCanvas:x,inlineImages:k,applyBackgroundColorToBlockedElements:H,onSerialize:G=>{Gi(G,ce)&&le.addIframe(G),Vi(G,ce)&&ae.trackLinkElement(G),Ir(G)&&ne.addShadowRoot(L.shadowRoot(G),document)},onIframeLoad:(G,V)=>{le.attachIframe(G,V),ne.observeAttachShadow(G)},onStylesheetLoad:(G,V)=>{ae.attachLinkElement(G,V)},keepIframeSrcFn:Z});if(!ue)return console.warn("Failed to snapshot the document");J({type:U.FullSnapshot,data:{node:ue,initialOffset:zi(window)}},_),ve.forEach(G=>G.unlock()),document.adoptedStyleSheets&&document.adoptedStyleSheets.length>0&&ae.adoptStyleSheets(document.adoptedStyleSheets,ce.getId(document))};try{const _=[],ue=V=>{var me;return T(ca)({mutationCb:se,mousemoveCb:(B,vt)=>J({type:U.IncrementalSnapshot,data:{source:vt,positions:B}}),mouseInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MouseInteraction,...B}}),scrollCb:Me,viewportResizeCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.ViewportResize,...B}}),inputCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Input,...B}}),mediaInteractionCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.MediaInteraction,...B}}),styleSheetRuleCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleSheetRule,...B}}),styleDeclarationCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.StyleDeclaration,...B}}),canvasMutationCb:de,fontCb:B=>J({type:U.IncrementalSnapshot,data:{source:D.Font,...B}}),selectionCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.Selection,...B}})},customElementCb:B=>{J({type:U.IncrementalSnapshot,data:{source:D.CustomElement,...B}})},blockClass:s,ignoreClass:u,ignoreSelector:m,maskTextClass:a,maskTextSelector:p,maskInputOptions:W,inlineStylesheet:i,sampling:S,recordDOM:v,recordCanvas:x,inlineImages:k,userTriggeredOnInput:M,collectFonts:$,doc:V,maskInputFn:d,maskTextFn:h,maskAttributeFn:y,keepIframeSrcFn:Z,blockSelector:f,slimDOMOptions:P,dataURLOptions:b,mirror:ce,iframeManager:le,stylesheetManager:ae,shadowDomManager:ne,processedNodeManager:X,canvasManager:Or,ignoreCSSAttributes:N,plugins:((me=R?.filter(B=>B.observer))==null?void 0:me.map(B=>({observer:B.observer,options:B.options,callback:vt=>J({type:U.Plugin,data:{plugin:B.name,payload:vt}})})))||[]},C)};le.addLoadListener(V=>{try{_.push(ue(V.contentDocument))}catch(me){console.warn(me)}});const G=()=>{tt(),_.push(ue(document)),ut=!0};return document.readyState==="interactive"||document.readyState==="complete"?G():(_.push(te("DOMContentLoaded",()=>{J({type:U.DomContentLoaded,data:{}}),A==="DOMContentLoaded"&&G()})),_.push(te("load",()=>{J({type:U.Load,data:{}}),A==="load"&&G()},window))),()=>{_.forEach(V=>V()),X.destroy(),ut=!1,Xo()}}catch(_){console.warn(_)}}Oe.addCustomEvent=(e,t)=>{if(!ut)throw new Error("please add custom event after start recording");J({type:U.Custom,data:{tag:e,payload:t}})};Oe.freezePage=()=>{ve.forEach(e=>e.freeze())};Oe.takeFullSnapshot=e=>{if(!ut)throw new Error("please take full snapshot after start recording");tt(e)};Oe.mirror=ce;var yi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(yi||(yi={}));const{addCustomEvent:xa}=Oe,{freezePage:Ra}=Oe,{takeFullSnapshot:Oa}=Oe;export{Oe as record}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/DqeYcxy-.js - -```js -import{_ as N,a as A,o as U,c as O}from"./HWAjEwF5.js";var L=function(a,e){return L=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,t){r.__proto__=t}||function(r,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(r[n]=t[n])},L(a,e)};function k(a,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");L(a,e);function r(){this.constructor=a}a.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var d=function(){return d=Object.assign||function(e){for(var r,t=1,n=arguments.length;t=a.length&&(a=void 0),{value:a&&a[t++],done:!a}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function D(a,e){var r=typeof Symbol=="function"&&a[Symbol.iterator];if(!r)return a;var t=r.call(a),n,o=[],i;try{for(;(e===void 0||e-- >0)&&!(n=t.next()).done;)o.push(n.value)}catch(c){i={error:c}}finally{try{n&&!n.done&&(r=t.return)&&r.call(t)}finally{if(i)throw i.error}}return o}function P(a,e,r){if(arguments.length===2)for(var t=0,n=e.length,o;t>6|192,e[r++]=n&63|128):(n&64512)==55296&&t+1>18|240,e[r++]=n>>12&63|128,e[r++]=n>>6&63|128,e[r++]=n&63|128):(e[r++]=n>>12|224,e[r++]=n>>6&63|128,e[r++]=n&63|128)}return Uint8Array.from(e)},R=-862048943,I=461845907,x=15,M=13,Q=5,B=-430675100,F=function(a,e){e===void 0&&(e=0);for(var r=j(a),t=r.length,n=t>>2,o=e,i=0;i>>0},X=function(a,e){var r=a,t=e;return r=Math.imul(r,R),r=m(r,x),r=Math.imul(r,I),t^=r,t=m(t,M),t=Math.imul(t,Q),t+B|0},Y=function(a){var e=a;return e^=e>>>16,e=Math.imul(e,-2048144789),e^=e>>>13,e=Math.imul(e,-1028477387),e^=e>>>16,e},m=function(a,e,r){r===void 0&&(r=32),e>r&&(e=e%r);var t=4294967295<>>0,n=(a&t)>>>0>>>r-e>>>0;return(a<>>0},q=function(a,e){e===void 0&&(e=0);var r=a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3];return J(r)},J=function(a){return(a&-16777216)>>>24|(a&16711680)>>>8|(a&65280)<<8|(a&255)<<24},H=function(a,e){var r,t;if(!(!e||e.length===0)){try{for(var n=y(e),o=n.next();!o.done;o=n.next()){var i=o.value;if(!i||!a||typeof a!="object")return;a=a[i]}}catch(c){r={error:c}}finally{try{o&&!o.done&&(t=n.return)&&t.call(n)}finally{if(r)throw r.error}}if(a)return a}},z="(\\d+)\\.(\\d+)",K="(\\d+)",$="(-(([-\\w]+\\.?)*))?",W="^".concat(z,"(\\.").concat(K).concat($,")?$"),Z=(function(){function a(e,r,t,n){n===void 0&&(n=void 0),this.major=e,this.minor=r,this.patch=t,this.preRelease=n}return a.parse=function(e){if(e){var r=new RegExp(W).exec(e);if(r){var t=Number(r[1]),n=Number(r[2]);if(!(isNaN(t)||isNaN(n))){var o=Number(r[4])||0,i=r[5]||void 0;return new a(t,n,o,i)}}}},a.prototype.compareTo=function(e){return this.major>e.major?1:this.majore.minor?1:this.minore.patch?1:this.patche.preRelease?1:this.preRelease=l&&u=b&&St;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return e>=t;default:return!1}},a.prototype.versionComparator=function(e,r,t){var n=e.compareTo(t);switch(r){case s.LESS_THAN:case s.VERSION_LESS_THAN:return n<0;case s.LESS_THAN_EQUALS:case s.VERSION_LESS_THAN_EQUALS:return n<=0;case s.GREATER_THAN:case s.VERSION_GREATER_THAN:return n>0;case s.GREATER_THAN_EQUALS:case s.VERSION_GREATER_THAN_EQUALS:return n>=0;default:return!1}},a.prototype.matchesRegex=function(e,r){return r.some(function(t){return!!new RegExp(t).exec(e)})},a.prototype.containsNone=function(e){return e.some(function(r){return r==="(none)"})},a.prototype.containsBooleans=function(e){return e.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},a.prototype.parseNumber=function(e){var r;return(r=Number(e))!==null&&r!==void 0?r:void 0},a.prototype.coerceString=function(e){if(e)return typeof e=="object"?JSON.stringify(e):String(e)},a.prototype.coerceStringArray=function(e){var r=this;if(Array.isArray(e)){var t=e;return t.map(function(i){return r.coerceString(i)}).filter(Boolean)}var n=String(e);try{var o=JSON.parse(n);if(Array.isArray(o)){var t=e;return t.map(function(c){return r.coerceString(c)}).filter(Boolean)}else return}catch{return}},a.prototype.isSetOperator=function(e){switch(e){case s.SET_IS:case s.SET_IS_NOT:case s.SET_CONTAINS:case s.SET_DOES_NOT_CONTAIN:case s.SET_CONTAINS_ANY:case s.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},a.prototype.setEquals=function(e,r){var t=new Set(e),n=new Set(r);return t.size===n.size&&P([],D(n),!1).every(function(o){return t.has(o)})},a.prototype.matchesSetContainsAll=function(e,r){var t,n;if(e.length{let e={};return a.forEach((r,t)=>e[r]=t),e})(re);String.fromCharCode.bind(String);typeof Uint8Array.from=="function"&&Uint8Array.from.bind(Uint8Array);(function(a){k(e,a);function e(r,t){var n=a.call(this,t)||this;return n.statusCode=r,Object.setPrototypeOf(n,e.prototype),n}return e})(Error);var te=1e3*60*60*24*2,ne=(function(){function a(){var e=this;this.dbs={},this.createStore=function(r){return N(e,void 0,void 0,function(){return A(this,function(t){switch(t.label){case 0:return[4,U(r,1,{upgrade:function(n){n.objectStoreNames.contains("eventTypesForSession")||n.createObjectStore("eventTypesForSession",{keyPath:"sessionId"})}})];case 1:return[2,t.sent()]}})})},this.openOrCreateDB=function(r){return N(e,void 0,void 0,function(){var t,n;return A(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(t="".concat(r.substring(0,10),"_amp_targeting"),[4,this.createStore(t)]);case 1:return n=o.sent(),this.dbs[r]=n,[2,n]}})})},this.updateEventListForSession=function(r){var t=r.sessionId,n=r.eventType,o=r.eventTime,i=r.loggerProvider,c=r.tx;return N(e,void 0,void 0,function(){var v,f,u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return l.trys.push([0,3,,4]),[4,c.store.get(t)];case 1:return v=l.sent(),f=v?v.eventTypes:{},u=f[n]||{},S=O(O({},f),(E={},E[n]=O(O({},u),(T={},T[o]={event_type:n},T)),E)),[4,c.store.put({sessionId:t,eventTypes:S})];case 2:return l.sent(),[2,S];case 3:return h=l.sent(),i.warn("Failed to store events for targeting ".concat(t,": ").concat(h)),[3,4];case 4:return[2,void 0]}})})},this.deleteOldSessionEventTypes=function(r){var t=r.currentSessionId,n=r.loggerProvider,o=r.tx;return N(e,void 0,void 0,function(){var i,c,v,f,u;return A(this,function(S){switch(S.label){case 0:return S.trys.push([0,6,,7]),[4,o.store.getAll()];case 1:i=S.sent(),c=0,S.label=2;case 2:return cte?[4,o.store.delete(v.sessionId)]:[3,4]):[3,5];case 3:S.sent(),S.label=4;case 4:return c++,[3,2];case 5:return[3,7];case 6:return u=S.sent(),n.warn("Failed to clear old session events for targeting: ".concat(u)),[3,7];case 7:return[2]}})})},this.storeEventTypeForSession=function(r){var t=r.loggerProvider,n=r.sessionId,o=r.eventType,i=r.eventTime,c=r.apiKey;return N(e,void 0,void 0,function(){var v,f,u,S;return A(this,function(h){switch(h.label){case 0:return h.trys.push([0,5,,6]),[4,this.openOrCreateDB(c)];case 1:return v=h.sent(),f=v.transaction("eventTypesForSession","readwrite"),f?[4,this.updateEventListForSession({sessionId:n,tx:f,loggerProvider:t,eventType:o,eventTime:i})]:[2];case 2:return u=h.sent(),[4,this.deleteOldSessionEventTypes({currentSessionId:n,tx:f,loggerProvider:t})];case 3:return h.sent(),[4,f.done];case 4:return h.sent(),[2,u];case 5:return S=h.sent(),t.warn("Failed to store events for targeting ".concat(n,": ").concat(S)),[3,6];case 6:return[2,void 0]}})})}}return a})(),ae=new ne,oe=(function(){function a(){var e=this;this.evaluateTargeting=function(r){var t=r.apiKey,n=r.loggerProvider,o=r.event,i=r.sessionId,c=r.userProperties,v=r.deviceId,f=r.flag;return N(e,void 0,void 0,function(){var u,S,h,E,T;return A(this,function(l){switch(l.label){case 0:return o&&o.time?[4,ae.storeEventTypeForSession({loggerProvider:n,apiKey:t,sessionId:i,eventType:o.event_type,eventTime:o.time})]:[3,2];case 1:return S=l.sent(),[3,3];case 2:S=void 0,l.label=3;case 3:return u=S,h=u&&new Set(Object.keys(u)),E={session_id:i,event:o,event_types:h&&Array.from(h),user:{device_id:v,user_properties:c}},T=this.evaluationEngine.evaluate(E,[f]),[2,T]}})})},this.evaluationEngine=new V}return a})(),ie=function(){var a=new oe;return{evaluateTargeting:a.evaluateTargeting.bind(a)}};const se=ie();var ce=se.evaluateTargeting;export{ce as evaluateTargeting}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/HWAjEwF5.js - -```js -import{aS as nl,ac as ln,a2 as il,A as Vi,a1 as ji,w as ol,m as Mn,J as Ha,a3 as al,aT as sl,z as ul,Z as ll,E as cl,aA as dl,ad as fl,G as vl,F as Hi}from"./3TW-MQft.js";function hl(t,e){for(var r=0;rn[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}class gl{anchor;#t=new Map;#r=new Map;#e=new Map;#n=new Set;#i=!0;constructor(e,r=!0){this.anchor=e,this.#i=r}#o=e=>{if(this.#t.has(e)){var r=this.#t.get(e),n=this.#r.get(r);if(n)nl(n),this.#n.delete(r);else{var i=this.#e.get(r);i&&(this.#r.set(r,i.effect),this.#e.delete(r),i.fragment.lastChild.remove(),this.anchor.before(i.fragment),n=i.effect)}for(const[o,a]of this.#t){if(this.#t.delete(o),o===e)break;const s=this.#e.get(a);s&&(ln(s.effect),this.#e.delete(a))}for(const[o,a]of this.#r){if(o===r||this.#n.has(o))continue;const s=()=>{if(Array.from(this.#t.values()).includes(o)){var u=document.createDocumentFragment();al(a,u),u.append(Vi()),this.#e.set(o,{effect:a,fragment:u})}else ln(a);this.#n.delete(o),this.#r.delete(o)};this.#i||!n?(this.#n.add(o),il(a,s,!1)):s()}}};#a=e=>{this.#t.delete(e);const r=Array.from(this.#t.values());for(const[n,i]of this.#e)r.includes(n)||(ln(i.effect),this.#e.delete(n))};ensure(e,r){var n=ol,i=sl();if(r&&!this.#r.has(e)&&!this.#e.has(e))if(i){var o=document.createDocumentFragment(),a=Vi();o.append(a),this.#e.set(e,{effect:ji(()=>r(a)),fragment:o})}else this.#r.set(e,ji(()=>r(this.anchor)));if(this.#t.set(n,e),i){for(const[s,l]of this.#r)s===e?n.unskip_effect(l):n.skip_effect(l);for(const[s,l]of this.#e)s===e?n.unskip_effect(l.effect):n.skip_effect(l.effect);n.oncommit(this.#o),n.ondiscard(this.#a)}else Mn&&(this.anchor=Ha),this.#o(n)}}function Dm(t,e,r=!1){var n;Mn&&(n=Ha,ll());var i=new gl(t),o=r?cl:0;function a(s,l){if(Mn){var u=dl(n);if(s!==parseInt(u.substring(1))){var c=fl();vl(c),i.anchor=c,Hi(!1),i.ensure(s,l),Hi(!0);return}}i.ensure(s,l)}ul(()=>{var s=!1;e((l,u=0)=>{s=!0,a(u,l)}),s||a(-1,null)},o)}const pl="modulepreload",ml=function(t){return"/"+t},Gi={},Qt=function(e,r,n){let i=Promise.resolve();if(r&&r.length>0){let l=function(u){return Promise.all(u.map(c=>Promise.resolve(c).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const a=document.querySelector("meta[property=csp-nonce]"),s=a?.nonce||a?.getAttribute("nonce");i=l(r.map(u=>{if(u=ml(u),u in Gi)return;Gi[u]=!0;const c=u.endsWith(".css"),f=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${u}"]${f}`))return;const d=document.createElement("link");if(d.rel=c?"stylesheet":pl,c||(d.as="script"),d.crossOrigin="",d.href=u,s&&d.setAttribute("nonce",s),document.head.appendChild(d),c)return new Promise((v,h)=>{d.addEventListener("load",v),d.addEventListener("error",()=>h(new Error(`Unable to preload CSS for ${u}`)))})}))}function o(a){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=a,window.dispatchEvent(s),!s.defaultPrevented)throw a}return i.then(a=>{for(const s of a||[])s.status==="rejected"&&o(s.reason);return e().catch(o)})};var Un=function(t,e){return Un=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},Un(t,e)};function Fe(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");Un(t,e);function r(){this.constructor=t}t.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var C=function(){return C=Object.assign||function(e){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(u[0]===6||u[0]===2)){r=0;continue}if(u[0]===3&&(!o||u[1]>o[0]&&u[1]=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function D(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),i,o=[],a;try{for(;(e===void 0||e-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(s){a={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return o}function $(t,e,r){if(r||arguments.length===2)for(var n=0,i=e.length,o;nAl)return!1;for(var e in t){var r=t[e];if(!$a(e,r))return!1}return!0},$a=function(t,e){var r,n;if(typeof t!="string")return!1;if(Array.isArray(e)){var i=!0;try{for(var o=q(e),a=o.next();!a.done;a=o.next()){var s=a.value;if(Array.isArray(s))return!1;if(typeof s=="object")i=i&&Fn(s);else if(!["number","string"].includes(typeof s))return!1;if(!i)return!1}}catch(l){r={error:l}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}}else{if(e==null)return!1;if(typeof e=="object")return Fn(e);if(!["number","string","boolean"].includes(typeof e))return!1}return!0},tr=(function(){function t(){this._propertySet=new Set,this._properties={}}return t.prototype.getUserProperties=function(){return C({},this._properties)},t.prototype.set=function(e,r){return this._safeSet(le.SET,e,r),this},t.prototype.setOnce=function(e,r){return this._safeSet(le.SET_ONCE,e,r),this},t.prototype.append=function(e,r){return this._safeSet(le.APPEND,e,r),this},t.prototype.prepend=function(e,r){return this._safeSet(le.PREPEND,e,r),this},t.prototype.postInsert=function(e,r){return this._safeSet(le.POSTINSERT,e,r),this},t.prototype.preInsert=function(e,r){return this._safeSet(le.PREINSERT,e,r),this},t.prototype.remove=function(e,r){return this._safeSet(le.REMOVE,e,r),this},t.prototype.add=function(e,r){return this._safeSet(le.ADD,e,r),this},t.prototype.unset=function(e){return this._safeSet(le.UNSET,e,Wi),this},t.prototype.clearAll=function(){return this._properties={},this._properties[le.CLEAR_ALL]=Wi,this},t.prototype._safeSet=function(e,r,n){if(this._validate(e,r,n)){var i=this._properties[e];return i===void 0&&(i={},this._properties[e]=i),i[r]=n,this._propertySet.add(r),!0}return!1},t.prototype._validate=function(e,r,n){return this._properties[le.CLEAR_ALL]!==void 0||this._propertySet.has(r)?!1:e===le.ADD?typeof n=="number":e!==le.UNSET&&e!==le.REMOVE?$a(r,n):!0},t})(),le;(function(t){t.SET="$set",t.SET_ONCE="$setOnce",t.ADD="$add",t.APPEND="$append",t.PREPEND="$prepend",t.REMOVE="$remove",t.PREINSERT="$preInsert",t.POSTINSERT="$postInsert",t.UNSET="$unset",t.CLEAR_ALL="$clearAll"})(le||(le={}));var Cl=[le.CLEAR_ALL,le.UNSET,le.SET,le.SET_ONCE,le.ADD,le.APPEND,le.PREPEND,le.PREINSERT,le.POSTINSERT,le.REMOVE],kl="Event tracked successfully",Pl="Unexpected error occurred",Ol="Event rejected due to exceeded retry count",Rl="Event skipped due to optOut config",xl="Event rejected due to missing API key",Nl="Invalid API key",Ll="Client not initialized",pe;(function(t){t.Unknown="unknown",t.Skipped="skipped",t.Success="success",t.RateLimit="rate_limit",t.PayloadTooLarge="payload_too_large",t.Invalid="invalid",t.Failed="failed",t.Timeout="Timeout",t.SystemError="SystemError"})(pe||(pe={}));var xt=function(t,e,r){return e===void 0&&(e=0),r===void 0&&(r=pe.Unknown),{event:t,code:e,message:r}},F=function(){var t="ampIntegrationContext";if(typeof globalThis<"u"&&typeof globalThis[t]<"u")return globalThis[t];if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},Dl=function(t){return t?(t^Math.random()*16>>t/4).toString(16):(String(1e7)+String(-1e3)+String(-4e3)+String(-8e3)+String(-1e11)).replace(/[018]/g,Je)},no=$([],D(Array(256).keys()),!1).map(function(t){return t.toString(16).padStart(2,"0")}),Je=function(t){var e,r=F();if(!(!((e=r?.crypto)===null||e===void 0)&&e.getRandomValues))return Dl(t);var n=r.crypto.getRandomValues(new Uint8Array(16));return n[6]=n[6]&15|64,n[8]=n[8]&63|128,$([],D(n.entries()),!1).map(function(i){var o=D(i,2),a=o[0],s=o[1];return[4,6,8,10].includes(a)?"-".concat(no[s]):no[s]}).join("")},Ml=(function(){function t(e){this.client=e,this.queue=[],this.applying=!1,this.plugins=[],this._optOutListeners=[]}return t.prototype.register=function(e,r){var n,i;return _(this,void 0,void 0,function(){return T(this,function(o){switch(o.label){case 0:return this.plugins.some(function(a){return a.name===e.name})?(this.loggerProvider.warn("Plugin with name ".concat(e.name," already exists, skipping registration")),[2]):(e.name===void 0&&(e.name=Je(),this.loggerProvider.warn(`Plugin name is undefined. - Generating a random UUID for plugin name: `.concat(e.name,`. - Set a name for the plugin to prevent it from being added multiple times.`))),e.type=(n=e.type)!==null&&n!==void 0?n:"enrichment",[4,(i=e.setup)===null||i===void 0?void 0:i.call(e,r,this.client)]);case 1:return o.sent(),this.plugins.push(e),[2]}})})},t.prototype.deregister=function(e,r){var n;return _(this,void 0,void 0,function(){var i,o;return T(this,function(a){switch(a.label){case 0:return i=this.plugins.findIndex(function(s){return s.name===e}),i===-1?(r.loggerProvider.warn("Plugin with name ".concat(e," does not exist, skipping deregistration")),[2]):(o=this.plugins[i],this.plugins.splice(i,1),[4,(n=o.teardown)===null||n===void 0?void 0:n.call(o)]);case 1:return a.sent(),[2]}})})},t.prototype.reset=function(e){this._clearOptOutListeners(),this.applying=!1;var r=this.plugins;r.map(function(n){var i;return(i=n.teardown)===null||i===void 0?void 0:i.call(n)}),this.plugins=[],this.client=e},t.prototype.push=function(e){var r=this;return new Promise(function(n){r.queue.push([e,n]),r.scheduleApply(0)})},t.prototype.scheduleApply=function(e){var r=this;this.applying||(this.applying=!0,setTimeout(function(){r.apply(r.queue.shift()).then(function(){r.applying=!1,r.queue.length>0&&r.scheduleApply(0)})},e))},t.prototype.apply=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a,s,l,v,h,u,c,f,d,v,h,g,m,E,p,y,b,S;return T(this,function(w){switch(w.label){case 0:if(!e)return[2];r=D(e,1),n=r[0],i=D(e,2),o=i[1],this.loggerProvider.log("Timeline.apply: Initial event",n),a=this.plugins.filter(function(A){return A.type==="before"}),w.label=1;case 1:w.trys.push([1,6,7,8]),s=q(a),l=s.next(),w.label=2;case 2:return l.done?[3,5]:(v=l.value,v.execute?[4,v.execute(C({},n))]:[3,4]);case 3:if(h=w.sent(),h===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by before plugin '".concat(String(v.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=h,this.loggerProvider.log("Timeline.apply: Event after before plugin '".concat(String(v.name),"', event: ").concat(JSON.stringify(n))),w.label=4;case 4:return l=s.next(),[3,2];case 5:return[3,8];case 6:return u=w.sent(),p={error:u},[3,8];case 7:try{l&&!l.done&&(y=s.return)&&y.call(s)}finally{if(p)throw p.error}return[7];case 8:c=this.plugins.filter(function(A){return A.type==="enrichment"||A.type===void 0}),w.label=9;case 9:w.trys.push([9,14,15,16]),f=q(c),d=f.next(),w.label=10;case 10:return d.done?[3,13]:(v=d.value,v.execute?[4,v.execute(C({},n))]:[3,12]);case 11:if(h=w.sent(),h===null)return this.loggerProvider.log("Timeline.apply: Event filtered out by enrichment plugin '".concat(String(v.name),"', event: ").concat(JSON.stringify(n))),o({event:n,code:0,message:""}),[2];n=h,this.loggerProvider.log("Timeline.apply: Event after enrichment plugin '".concat(String(v.name),"', event: ").concat(JSON.stringify(n))),w.label=12;case 12:return d=f.next(),[3,10];case 13:return[3,16];case 14:return g=w.sent(),b={error:g},[3,16];case 15:try{d&&!d.done&&(S=f.return)&&S.call(f)}finally{if(b)throw b.error}return[7];case 16:return m=this.plugins.filter(function(A){return A.type==="destination"}),this.loggerProvider.log("Timeline.apply: Final event before destinations, event: ".concat(JSON.stringify(n))),E=m.map(function(A){var I=C({},n);return A.execute(I).catch(function(k){return xt(I,0,String(k))})}),Promise.all(E).then(function(A){var I=D(A,1),k=I[0],x=k||xt(n,100,"Event not tracked, no destination plugins on the instance");o(x)}),[2]}})})},t.prototype.flush=function(){return _(this,void 0,void 0,function(){var e,r,n,i=this;return T(this,function(o){switch(o.label){case 0:return e=this.queue,this.queue=[],[4,Promise.all(e.map(function(a){return i.apply(a)}))];case 1:return o.sent(),r=this.plugins.filter(function(a){return a.type==="destination"}),n=r.map(function(a){return a.flush&&a.flush()}),[4,Promise.all(n)];case 2:return o.sent(),[2]}})})},t.prototype.addOptOutListener=function(e){this._optOutListeners.push(e)},t.prototype._clearOptOutListeners=function(){this._optOutListeners=[]},t.prototype.onIdentityChanged=function(e){this.plugins.forEach(function(r){var n;(n=r.onIdentityChanged)===null||n===void 0||n.call(r,e)})},t.prototype.onSessionIdChanged=function(e){this.plugins.forEach(function(r){var n;(n=r.onSessionIdChanged)===null||n===void 0||n.call(r,e)})},t.prototype.onOptOutChanged=function(e){this.plugins.forEach(function(r){var n;(n=r.onOptOutChanged)===null||n===void 0||n.call(r,e)}),this._callOptOutListeners(e)},t.prototype._callOptOutListeners=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a,s,l;return T(this,function(u){switch(u.label){case 0:u.trys.push([0,7,8,9]),r=q(this._optOutListeners),n=r.next(),u.label=1;case 1:if(n.done)return[3,6];i=n.value,u.label=2;case 2:return u.trys.push([2,4,,5]),[4,i(e)];case 3:return u.sent(),[3,5];case 4:return o=u.sent(),this.loggerProvider.error("Error calling optOut listener",o),[3,5];case 5:return n=r.next(),[3,1];case 6:return[3,9];case 7:return a=u.sent(),s={error:a},[3,9];case 8:try{n&&!n.done&&(l=r.return)&&l.call(r)}finally{if(s)throw s.error}return[7];case 9:return[2]}})})},t.prototype.onReset=function(){this.plugins.forEach(function(e){var r;(r=e.onReset)===null||r===void 0||r.call(e)})},t})(),Ul=function(t,e,r){var n=typeof t=="string"?{event_type:t}:t;return C(C(C({},n),r),e&&{event_properties:e})},pi=function(t,e){var r=C(C({},e),{event_type:xe.IDENTIFY,user_properties:t.getUserProperties()});return r},Fl=function(t,e,r,n){var i,o=C(C({},n),{event_type:xe.GROUP_IDENTIFY,group_properties:r.getUserProperties(),groups:(i={},i[t]=e,i)});return o},ql=function(t,e,r){var n,i=new tr;i.set(t,e);var o=C(C({},r),{event_type:xe.IDENTIFY,user_properties:i.getUserProperties(),groups:(n={},n[t]=e,n)});return o},Bl=function(t,e){return C(C({},e),{event_type:xe.REVENUE,event_properties:t.getEventProperties()})},Te=function(t){return{promise:t||Promise.resolve()}},Vl=(function(){function t(e){e===void 0&&(e="$default"),this.initializing=!1,this.isReady=!1,this.q=[],this.dispatchQ=[],this.logEvent=this.track.bind(this),this.timeline=new Ml(this),this.name=e}return t.prototype._init=function(e){return _(this,void 0,void 0,function(){return T(this,function(r){switch(r.label){case 0:return this.config=e,this.timeline.reset(this),this.timeline.loggerProvider=this.config.loggerProvider,[4,this.runQueuedFunctions("q")];case 1:return r.sent(),this.isReady=!0,[2]}})})},t.prototype.runQueuedFunctions=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a,s,l,u;return T(this,function(c){switch(c.label){case 0:r=this[e],this[e]=[],c.label=1;case 1:c.trys.push([1,8,9,10]),n=q(r),i=n.next(),c.label=2;case 2:return i.done?[3,7]:(o=i.value,a=o(),a&&"promise"in a?[4,a.promise]:[3,4]);case 3:return c.sent(),[3,6];case 4:return[4,a];case 5:c.sent(),c.label=6;case 6:return i=n.next(),[3,2];case 7:return[3,10];case 8:return s=c.sent(),l={error:s},[3,10];case 9:try{i&&!i.done&&(u=n.return)&&u.call(n)}finally{if(l)throw l.error}return[7];case 10:return this[e].length?[4,this.runQueuedFunctions(e)]:[3,12];case 11:c.sent(),c.label=12;case 12:return[2]}})})},t.prototype.track=function(e,r,n){var i=Ul(e,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),Te(this.dispatch(i))},t.prototype.identify=function(e,r){var n=pi(e,r);return this.userProperties=this.getOperationAppliedUserProperties(n.user_properties),Te(this.dispatch(n))},t.prototype.groupIdentify=function(e,r,n,i){var o=Fl(e,r,n,i);return Te(this.dispatch(o))},t.prototype.setGroup=function(e,r,n){var i=ql(e,r,n);return this.userProperties=this.getOperationAppliedUserProperties(i.user_properties),Te(this.dispatch(i))},t.prototype.revenue=function(e,r){var n=Bl(e,r);return Te(this.dispatch(n))},t.prototype.add=function(e){return this.isReady?this._addPlugin(e):(this.q.push(this._addPlugin.bind(this,e)),Te())},t.prototype._addPlugin=function(e){return Te(this.timeline.register(e,this.config))},t.prototype.remove=function(e){return this.isReady?this._removePlugin(e):(this.q.push(this._removePlugin.bind(this,e)),Te())},t.prototype._removePlugin=function(e){return Te(this.timeline.deregister(e,this.config))},t.prototype.dispatchWithCallback=function(e,r){if(!this.isReady)return r(xt(e,0,Ll));this.process(e).then(r)},t.prototype.dispatch=function(e){return _(this,void 0,void 0,function(){var r=this;return T(this,function(n){return this.isReady?[2,this.process(e)]:[2,new Promise(function(i){r.dispatchQ.push(r.dispatchWithCallback.bind(r,e,i))})]})})},t.prototype.getOperationAppliedUserProperties=function(e){var r,n=(r=this.userProperties)!==null&&r!==void 0?r:{},i=C({},n);if(e===void 0)return i;var o={};return Object.keys(e).forEach(function(a){Object.values(ct).includes(a)||(o[a]=e[a])}),Cl.forEach(function(a){if(Object.keys(e).includes(a)){var s=e[a];switch(a){case ct.CLEAR_ALL:Object.keys(i).forEach(function(l){delete i[l]});break;case ct.UNSET:Object.keys(s).forEach(function(l){delete i[l]});break;case ct.SET:Object.assign(i,s);break}}}),Object.assign(i,o),i},t.prototype.process=function(e){return _(this,void 0,void 0,function(){var i,r,n,i;return T(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),this.config.optOut?[2,xt(e,0,Rl)]:(e.event_type===xe.IDENTIFY&&this.timeline.onIdentityChanged({userProperties:this.userProperties}),[4,this.timeline.push(e)]);case 1:return i=o.sent(),i.code===200?this.config.loggerProvider.log(i.message):i.code===100?this.config.loggerProvider.warn(i.message):this.config.loggerProvider.error(i.message),[2,i];case 2:return r=o.sent(),n=String(r),this.config.loggerProvider.error(n),i=xt(e,0,n),[2,i];case 3:return[2]}})})},t.prototype.setOptOut=function(e){if(!this.isReady){this.q.push(this._setOptOut.bind(this,!!e));return}this._setOptOut(e)},t.prototype._setOptOut=function(e){this.config.optOut!==e&&(this.config.optOut=!!e,this.timeline.onOptOutChanged(e))},t.prototype.flush=function(){return Te(this.timeline.flush())},t.prototype.plugin=function(e){var r=this.timeline.plugins.find(function(n){return n.name===e});if(r===void 0){this.config.loggerProvider.debug("Cannot find plugin with name ".concat(e));return}return r},t.prototype.plugins=function(e){return this.timeline.plugins.filter(function(r){return r instanceof e})},t})(),jl=(function(){function t(){this.productId="",this.quantity=1,this.price=0}return t.prototype.setProductId=function(e){return this.productId=e,this},t.prototype.setQuantity=function(e){return e>0&&(this.quantity=e),this},t.prototype.setPrice=function(e){return this.price=e,this},t.prototype.setRevenueType=function(e){return this.revenueType=e,this},t.prototype.setCurrency=function(e){return this.currency=e,this},t.prototype.setRevenue=function(e){return this.revenue=e,this},t.prototype.setReceipt=function(e){return this.receipt=e,this},t.prototype.setReceiptSig=function(e){return this.receiptSig=e,this},t.prototype.setEventProperties=function(e){try{var r=JSON.parse(JSON.stringify(e));Fn(r)&&(this.properties=r)}catch{}return this},t.prototype.getEventProperties=function(){var e=this.properties?C({},this.properties):{};return e[$e.REVENUE_PRODUCT_ID]=this.productId,e[$e.REVENUE_QUANTITY]=this.quantity,e[$e.REVENUE_PRICE]=this.price,e[$e.REVENUE_TYPE]=this.revenueType,e[$e.REVENUE_CURRENCY]=this.currency,e[$e.REVENUE]=this.revenue,e[$e.RECEIPT]=this.receipt,e[$e.RECEIPT_SIG]=this.receiptSig,e},t})(),$e;(function(t){t.REVENUE_PRODUCT_ID="$productId",t.REVENUE_QUANTITY="$quantity",t.REVENUE_PRICE="$price",t.REVENUE_TYPE="$revenueType",t.REVENUE_CURRENCY="$currency",t.REVENUE="$revenue",t.RECEIPT="$receipt",t.RECEIPT_SIG="$receiptSig"})($e||($e={}));var Hl=function(t,e){var r=Math.max(e,1);return t.reduce(function(n,i,o){var a=Math.floor(o/r);return n[a]||(n[a]=[]),n[a].push(i),n},[])},ke;(function(t){t[t.None=0]="None",t[t.Error=1]="Error",t[t.Warn=2]="Warn",t[t.Verbose=3]="Verbose",t[t.Debug=4]="Debug"})(ke||(ke={}));var pr="Amplitude Logger ",Dt=(function(){function t(){this.logLevel=ke.None}return t.prototype.disable=function(){this.logLevel=ke.None},t.prototype.enable=function(e){e===void 0&&(e=ke.Warn),this.logLevel=e},t.prototype.log=function(){for(var e=[],r=0;r=200&&t<300}var Rr=function(t){t===void 0&&(t=0);var e=new Error().stack||"";return e.split(` -`).slice(2+t).map(function(r){return r.trim()})},ae=function(t){return function(){var e=C({},t.config),r=e.loggerProvider,n=e.logLevel;return{logger:r,logLevel:n}}},Kl=function(t,e){var r,n;e=e.replace(/\[(\w+)\]/g,".$1"),e=e.replace(/^\./,"");try{for(var i=q(e.split(".")),o=i.next();!o.done;o=i.next()){var a=o.value;if(a in t)t=t[a];else return}}catch(s){r={error:s}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return t},se=function(t,e){return function(){var r,n,i={};try{for(var o=q(e),a=o.next();!a.done;a=o.next()){var s=a.value;i[s]=Kl(t,s)}}catch(l){r={error:l}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return i}},Q=function(t,e,r,n,i){return i===void 0&&(i=null),function(){for(var o=[],a=0;a0&&Promise.all(n.map(function(a){return i.execute(a)})).catch(),[2,Promise.resolve(void 0)]}})})},t.prototype.execute=function(e){var r=this;return e.insert_id||(e.insert_id=Je()),new Promise(function(n){var i={event:e,attempts:0,callback:function(o){return n(o)},timeout:0};r.queue.push(i),r.schedule(r.config.flushIntervalMillis),r.saveEvents()})},t.prototype.removeEventsExceedFlushMaxRetries=function(e){var r=this;return e.filter(function(n){return n.attempts+=1,n.attemptsthis.scheduledTimeout)){this.scheduleId&&clearTimeout(this.scheduleId),this.scheduledTimeout=e,this.scheduleId=setTimeout(function(){r.queue=r.queue.map(function(n){return n.timeout=0,n}),r.flush(!0)},e);return}},t.prototype.resetSchedule=function(){this.scheduleId=null,this.scheduledTimeout=0},t.prototype.flush=function(e){return e===void 0&&(e=!1),_(this,void 0,void 0,function(){var r,n,i,o=this;return T(this,function(a){switch(a.label){case 0:return this.config.offline?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush while offline."),[2]):this.flushId?(this.resetSchedule(),this.config.loggerProvider.debug("Skipping flush because previous flush has not resolved."),[2]):(this.flushId=this.scheduleId,this.resetSchedule(),r=[],n=[],this.queue.forEach(function(s){return s.timeout===0?r.push(s):n.push(s)}),i=Hl(r,this.config.flushQueueSize),[4,i.reduce(function(s,l){return _(o,void 0,void 0,function(){return T(this,function(u){switch(u.label){case 0:return[4,s];case 1:return u.sent(),[4,this.send(l,e)];case 2:return[2,u.sent()]}})})},Promise.resolve())]);case 1:return a.sent(),this.flushId=null,this.scheduleEvents(this.queue),[2]}})})},t.prototype.send=function(e,r){var n;return r===void 0&&(r=!0),_(this,void 0,void 0,function(){var i,o,a,s,l,u;return T(this,function(c){switch(c.label){case 0:if(!this.config.apiKey)return[2,this.fulfillRequest(e,400,xl)];i={api_key:this.config.apiKey,events:e.map(function(f){var d=f.event;d.extra;var v=Gr(d,["extra"]);return v}),options:{min_id_length:this.config.minIdLength},client_upload_time:new Date().toISOString(),request_metadata:this.config.requestMetadata},this.config.requestMetadata=new Wl,c.label=1;case 1:return c.trys.push([1,3,,4]),o=Ya(this.config.serverUrl,this.config.serverZone,this.config.useBatch).serverUrl,a=$l(o,this.config.enableRequestBodyCompression),[4,this.config.transportProvider.send(o,i,this.config._enableRequestBodyCompressionExperimental&&a)];case 2:return s=c.sent(),s===null?(this.fulfillRequest(e,0,Pl),[2]):r?(this.handleResponse(s,e),[3,4]):("body"in s?this.fulfillRequest(e,s.statusCode,"".concat(s.status,": ").concat(jt(s))):this.fulfillRequest(e,s.statusCode,s.status),[2]);case 3:return l=c.sent(),u=Xl(l),this.config.loggerProvider.error(u),(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful.from.catch.error",{events:e.map(function(f){return f.event.event_type}),message:u,stack_trace:Rr()}),this.handleResponse({status:pe.Failed,statusCode:0},e),[3,4];case 4:return[2]}})})},t.prototype.handleResponse=function(e,r){var n;qn(e.statusCode)||(n=this.diagnosticsClient)===null||n===void 0||n.recordEvent("analytics.events.unsuccessful",{events:r.map(function(o){return o.event.event_type}),code:e.statusCode,status:e.status,body:jt(e),stack_trace:Rr()});var i=e.status;switch(i){case pe.Success:{this.handleSuccessResponse(e,r);break}case pe.Invalid:{this.handleInvalidResponse(e,r);break}case pe.PayloadTooLarge:{this.handlePayloadTooLargeResponse(e,r);break}case pe.RateLimit:{this.handleRateLimitResponse(e,r);break}default:{this.config.loggerProvider.warn(`{code: 0, error: "Status '`.concat(i,"' provided for ").concat(r.length,' events"}')),this.handleOtherResponse(r);break}}},t.prototype.handleSuccessResponse=function(e,r){this.fulfillRequest(r,e.statusCode,kl)},t.prototype.handleInvalidResponse=function(e,r){var n=this;if(e.body.missingField||e.body.error.startsWith(Nl)){this.fulfillRequest(r,e.statusCode,e.body.error);return}var i=$($($($([],D(Object.values(e.body.eventsWithInvalidFields)),!1),D(Object.values(e.body.eventsWithMissingFields)),!1),D(Object.values(e.body.eventsWithInvalidIdLengths)),!1),D(e.body.silencedEvents),!1).flat(),o=new Set(i),a=r.filter(function(l,u){if(o.has(u)){n.fulfillRequest([l],e.statusCode,e.body.error);return}return!0});a.length>0&&this.config.loggerProvider.warn(jt(e));var s=this.removeEventsExceedFlushMaxRetries(a);this.scheduleEvents(s)},t.prototype.handlePayloadTooLargeResponse=function(e,r){if(r.length===1){this.fulfillRequest(r,e.statusCode,e.body.error);return}this.config.loggerProvider.warn(jt(e)),this.config.flushQueueSize/=2;var n=this.removeEventsExceedFlushMaxRetries(r);this.scheduleEvents(n)},t.prototype.handleRateLimitResponse=function(e,r){var n=this,i=Object.keys(e.body.exceededDailyQuotaUsers),o=Object.keys(e.body.exceededDailyQuotaDevices),a=e.body.throttledEvents,s=new Set(i),l=new Set(o),u=new Set(a),c=r.filter(function(d,v){if(d.event.user_id&&s.has(d.event.user_id)||d.event.device_id&&l.has(d.event.device_id)){n.fulfillRequest([d],e.statusCode,e.body.error);return}return u.has(v)&&(d.timeout=n.throttleTimeout),!0});c.length>0&&this.config.loggerProvider.warn(jt(e));var f=this.removeEventsExceedFlushMaxRetries(c);this.scheduleEvents(f)},t.prototype.handleOtherResponse=function(e){var r=this,n=e.map(function(o){return o.timeout=o.attempts*r.retryTimeout,o}),i=this.removeEventsExceedFlushMaxRetries(n);this.scheduleEvents(i)},t.prototype.fulfillRequest=function(e,r,n){var i,o,a;qn(r)?(a=this.diagnosticsClient)===null||a===void 0||a.increment("analytics.events.sent",e.length):((i=this.diagnosticsClient)===null||i===void 0||i.increment("analytics.events.dropped",e.length),(o=this.diagnosticsClient)===null||o===void 0||o.recordEvent("analytics.events.dropped",{events:e.map(function(s){return s.event.event_type}),code:r,message:n,stack_trace:Rr()})),this.removeEvents(e),e.forEach(function(s){return s.callback(xt(s.event,r,n))})},t.prototype.saveEvents=function(){if(this.config.storageProvider){var e=this.queue.map(function(r){return r.event});this.config.storageProvider.set(this.storageKey,e)}},t.prototype.removeEvents=function(e){this.queue=this.queue.filter(function(r){return!e.some(function(n){return n.event.insert_id===r.event.insert_id})}),this.saveEvents()},t})(),Jl=(function(){function t(){}return t.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:Ql(),platform:"Web",os:void 0,deviceModel:void 0}},t})(),Ql=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},Zl=(function(){function t(){this.queue=[]}return t.prototype.logEvent=function(e){this.receiver?this.receiver(e):this.queue.length<512&&this.queue.push(e)},t.prototype.setEventReceiver=function(e){this.receiver=e,this.queue.length>0&&(this.queue.forEach(function(r){e(r)}),this.queue=[])},t})(),ot=function(){return ot=Object.assign||function(e){for(var r,n=1,i=arguments.length;n=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function io(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),i,o=[],a;try{for(;(e===void 0||e-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(s){a={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return o}var Nr=function(t,e){var r,n,i=["string","number","boolean","undefined"],o=typeof t,a=typeof e;if(o!==a)return!1;try{for(var s=xr(i),l=s.next();!l.done;l=s.next()){var u=l.value;if(u===o)return t===e}}catch(m){r={error:m}}finally{try{l&&!l.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}if(t==null&&e==null)return!0;if(t==null||e==null||t.length!==e.length)return!1;var c=Array.isArray(t),f=Array.isArray(e);if(c!==f)return!1;if(c&&f){for(var d=0;dt},Qa=function(t,e,r){return e===void 0&&(e=""),r===void 0&&(r=10),[Qr,e,t.substring(0,r)].filter(Boolean).join("_")},sc=function(t){return"".concat(Qr.toLowerCase(),"_").concat(t.substring(0,6))},uc=function(){var t,e,r,n;if(typeof navigator>"u")return"";var i=navigator.userLanguage;return(n=(r=(e=(t=navigator.languages)===null||t===void 0?void 0:t[0])!==null&&e!==void 0?e:navigator.language)!==null&&r!==void 0?r:i)!==null&&n!==void 0?n:""},Kr=function(){var t,e=F();if(!(!((t=e?.location)===null||t===void 0)&&t.search))return{};var r=e.location.search.substring(1).split("&").filter(Boolean),n=r.reduce(function(i,o){var a=o.split("=",2),s=oo(a[0]),l=oo(a[1]);return l&&(i[s]=l),i},{});return n},oo=function(t){t===void 0&&(t="");try{return decodeURIComponent(t)}catch{return""}},Bn=function(t,e){return!e||!e.length?!0:e.some(function(r){return typeof r=="string"?t===r:t.match(r)})},dt=function(t,e){var r=t;try{r=decodeURI(t)}catch(n){e?.error("Malformed URI sequence: ",n)}return r},Vn=function(t){var e=0;if(t.length===0)return e;for(var r=0;r1&&((r=this.config.diagnosticsClient)===null||r===void 0||r.recordEvent("cookies.duplicate",{cookies:s.map(function(h){return h.domain})}),(n=this.config.diagnosticsClient)===null||n===void 0||n.increment("cookies.duplicate.occurrence.cookieStore"));try{for(l=q(s),u=l.next();!u.done;u=l.next())if(c=u.value,es(c.domain,this.options.domain))return[2,c.value]}catch(h){f={error:h}}finally{try{u&&!u.done&&(d=l.return)&&d.call(l)}finally{if(f)throw f.error}}}v.label=3;case 3:return[3,5];case 4:return v.sent(),[3,5];case 5:return[2,this.getRawSync(e)]}})})},t.prototype.getRawSync=function(e){var r=this,n,i,o=F(),a=((i=(n=o?.document)===null||n===void 0?void 0:n.cookie.split("; "))!==null&&i!==void 0?i:[]).filter(function(u){return u.indexOf(e+"=")===0}),s=void 0,l=this.config.duplicateResolverFn;if(typeof l=="function"&&a.length>1&&(s=a.find(function(u){var c;try{var f=l(u.substring(e.length+1));return f||(c=r.config.diagnosticsClient)===null||c===void 0||c.increment("cookies.duplicate.occurrence.document.cookie"),f}catch{return!1}})),s||(s=a[0]),!!s)return s.substring(e.length+1)},t.prototype.set=function(e,r){return _(this,void 0,void 0,function(){return T(this,function(n){return this.setSync(e,r),[2]})})},t.prototype.setSync=function(e,r){var n;try{var i=(n=this.options.expirationDays)!==null&&n!==void 0?n:0,o=r!==null?i:-1,a=void 0;if(o){var s=new Date;s.setTime(s.getTime()+o*24*60*60*1e3),a=s}var l="".concat(e,"=").concat(btoa(encodeURIComponent(JSON.stringify(r))));a&&(l+="; expires=".concat(a.toUTCString())),l+="; path=/",this.options.domain&&(l+="; domain=".concat(this.options.domain)),this.options.secure&&(l+="; Secure"),this.options.sameSite&&(l+="; SameSite=".concat(this.options.sameSite));var u=F();u?.document&&(u.document.cookie=l)}catch(f){var c=f instanceof Error?f.message:String(f);console.error("Amplitude Logger [Error]: Failed to set cookie for key: ".concat(e,". Error: ").concat(c))}},t.prototype.remove=function(e){return _(this,void 0,void 0,function(){return T(this,function(r){switch(r.label){case 0:return[4,this.set(e,null)];case 1:return r.sent(),[2]}})})},t.prototype.reset=function(){return _(this,void 0,void 0,function(){return T(this,function(e){return[2]})})},t.isDomainWritable=function(e){return _(this,void 0,void 0,function(){var r,n,i,o;return T(this,function(a){switch(a.label){case 0:if(t.cachedTlds[e])return[2,!0];r={domain:"."+e},n="AMP_TLDTEST",i=new t(r),a.label=1;case 1:return a.trys.push([1,3,,4]),[4,i.transaction(n,function(s){if(t.cachedTlds[e])return!0;try{s.set(1);var l=!!s.get();return l&&(t.cachedTlds[e]=!0),l}finally{s.set(null)}})];case 2:return o=a.sent(),[2,!!o];case 3:return a.sent(),[2,!1];case 4:return[2]}})})},t.prototype.transaction=function(e,r){return _(this,void 0,void 0,function(){var n,i,o=this;return T(this,function(a){switch(a.label){case 0:if(n=lc(),i=function(){var s={get:function(){return o.getSync(e)},set:function(l){return o.setSync(e,l)}};return r(s)},!n)return[2,i()];a.label=1;case 1:return a.trys.push([1,3,,4]),[4,n.request("com.amplitude:cookie-lock:".concat(e),i)];case 2:return[2,a.sent()];case 3:return a.sent(),[2,i()];case 4:return[2]}})})},t.cachedTlds={},t})(),cc=function(t){try{return decodeURIComponent(atob(t))}catch{return}},dc=function(t){try{return decodeURIComponent(atob(decodeURIComponent(t)))}catch{return}},Za=function(t){var e;return(e=cc(t))!==null&&e!==void 0?e:dc(t)},es=function(t,e){if(t===""&&e==="")return!0;if(!t||!e)return!1;var r=t.startsWith(".")?t.substring(1):t,n=e.startsWith(".")?e.substring(1):e;return r.toLowerCase()===n.toLowerCase()},so=function(t,e,r){return e===void 0&&(e=""),r===void 0&&(r=10),[Qr,e,t.substring(0,r)].filter(Boolean).join("_")},en=(function(){function t(e){this.storage=e}return t.prototype.isEnabled=function(){return _(this,void 0,void 0,function(){var e,r,n,i;return T(this,function(o){switch(o.label){case 0:if(!this.storage)return[2,!1];e=String(Date.now()),r=new t(this.storage),n="AMP_TEST",o.label=1;case 1:return o.trys.push([1,4,5,7]),[4,r.set(n,e)];case 2:return o.sent(),[4,r.get(n)];case 3:return i=o.sent(),[2,i===e];case 4:return o.sent(),[2,!1];case 5:return[4,r.remove(n)];case 6:return o.sent(),[7];case 7:return[2]}})})},t.prototype.get=function(e){return _(this,void 0,void 0,function(){var r;return T(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,this.getRaw(e)];case 1:return r=n.sent(),r?[2,JSON.parse(r)]:[2,void 0];case 2:return n.sent(),console.error("[Amplitude] Error: Could not get value from storage"),[2,void 0];case 3:return[2]}})})},t.prototype.getRaw=function(e){var r;return _(this,void 0,void 0,function(){return T(this,function(n){return[2,((r=this.storage)===null||r===void 0?void 0:r.getItem(e))||void 0]})})},t.prototype.set=function(e,r){var n;return _(this,void 0,void 0,function(){return T(this,function(i){try{(n=this.storage)===null||n===void 0||n.setItem(e,JSON.stringify(r))}catch{}return[2]})})},t.prototype.remove=function(e){var r;return _(this,void 0,void 0,function(){return T(this,function(n){try{(r=this.storage)===null||r===void 0||r.removeItem(e)}catch{}return[2]})})},t.prototype.reset=function(){var e;return _(this,void 0,void 0,function(){return T(this,function(r){try{(e=this.storage)===null||e===void 0||e.clear()}catch{}return[2]})})},t})(),fc=10,vc=1,re={TAGS:"tags",COUNTERS:"counters",HISTOGRAMS:"histograms",EVENTS:"events",INTERNAL:"internal"},uo={LAST_FLUSH_TIMESTAMP:"last_flush_timestamp"},lo=(function(){function t(e,r){this.dbPromise=null,this.logger=r,this.dbName="AMP_diagnostics_".concat(e.substring(0,10))}return t.isSupported=function(){var e;return((e=F())===null||e===void 0?void 0:e.indexedDB)!==void 0},t.prototype.getDB=function(){return _(this,void 0,void 0,function(){return T(this,function(e){return this.dbPromise||(this.dbPromise=this.openDB()),[2,this.dbPromise]})})},t.prototype.openDB=function(){var e=this;return new Promise(function(r,n){var i=indexedDB.open(e.dbName,vc);i.onerror=function(){e.dbPromise=null,n(new Error("Failed to open IndexedDB"))},i.onsuccess=function(){var o=i.result;o.onclose=function(){e.dbPromise=null,e.logger.debug("DiagnosticsStorage: DB connection closed.")},o.onerror=function(a){e.logger.debug("DiagnosticsStorage: A global database error occurred.",a),o.close()},r(o)},i.onupgradeneeded=function(o){var a=o.target.result;e.createTables(a)}})},t.prototype.createTables=function(e){if(e.objectStoreNames.contains(re.TAGS)||e.createObjectStore(re.TAGS,{keyPath:"key"}),e.objectStoreNames.contains(re.COUNTERS)||e.createObjectStore(re.COUNTERS,{keyPath:"key"}),e.objectStoreNames.contains(re.HISTOGRAMS)||e.createObjectStore(re.HISTOGRAMS,{keyPath:"key"}),!e.objectStoreNames.contains(re.EVENTS)){var r=e.createObjectStore(re.EVENTS,{keyPath:"id",autoIncrement:!0});r.createIndex("time_idx","time",{unique:!1})}e.objectStoreNames.contains(re.INTERNAL)||e.createObjectStore(re.INTERNAL,{keyPath:"key"})},t.prototype.setTags=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a=this;return T(this,function(s){switch(s.label){case 0:return s.trys.push([0,2,,3]),Object.entries(e).length===0?[2]:[4,this.getDB()];case 1:return r=s.sent(),n=r.transaction([re.TAGS],"readwrite"),i=n.objectStore(re.TAGS),[2,new Promise(function(l){var u=Object.entries(e);n.oncomplete=function(){l()},n.onabort=function(c){a.logger.debug("DiagnosticsStorage: Failed to set tags",c),l()},u.forEach(function(c){var f=D(c,2),d=f[0],v=f[1],h=i.put({key:d,value:v});h.onerror=function(g){a.logger.debug("DiagnosticsStorage: Failed to set tag",d,v,g)}})})];case 2:return o=s.sent(),this.logger.debug("DiagnosticsStorage: Failed to set tags",o),[3,3];case 3:return[2]}})})},t.prototype.incrementCounters=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a=this;return T(this,function(s){switch(s.label){case 0:return s.trys.push([0,2,,3]),Object.entries(e).length===0?[2]:[4,this.getDB()];case 1:return r=s.sent(),n=r.transaction([re.COUNTERS],"readwrite"),i=n.objectStore(re.COUNTERS),[2,new Promise(function(l){var u=Object.entries(e);n.oncomplete=function(){l()},n.onabort=function(c){a.logger.debug("DiagnosticsStorage: Failed to increment counters",c),l()},u.forEach(function(c){var f=D(c,2),d=f[0],v=f[1],h=i.get(d);h.onsuccess=function(){var g=h.result,m=g?g.value:0,E=i.put({key:d,value:m+v});E.onerror=function(p){a.logger.debug("DiagnosticsStorage: Failed to update counter",d,p)}},h.onerror=function(g){a.logger.debug("DiagnosticsStorage: Failed to read existing counter",d,g)}})})];case 2:return o=s.sent(),this.logger.debug("DiagnosticsStorage: Failed to increment counters",o),[3,3];case 3:return[2]}})})},t.prototype.setHistogramStats=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a=this;return T(this,function(s){switch(s.label){case 0:return s.trys.push([0,2,,3]),Object.entries(e).length===0?[2]:[4,this.getDB()];case 1:return r=s.sent(),n=r.transaction([re.HISTOGRAMS],"readwrite"),i=n.objectStore(re.HISTOGRAMS),[2,new Promise(function(l){var u=Object.entries(e);n.oncomplete=function(){l()},n.onabort=function(c){a.logger.debug("DiagnosticsStorage: Failed to set histogram stats",c),l()},u.forEach(function(c){var f=D(c,2),d=f[0],v=f[1],h=i.get(d);h.onsuccess=function(){var g=h.result,m;g?m={key:d,count:g.count+v.count,min:Math.min(g.min,v.min),max:Math.max(g.max,v.max),sum:g.sum+v.sum}:m={key:d,count:v.count,min:v.min,max:v.max,sum:v.sum};var E=i.put(m);E.onerror=function(p){a.logger.debug("DiagnosticsStorage: Failed to set histogram stats",d,p)}},h.onerror=function(g){a.logger.debug("DiagnosticsStorage: Failed to read existing histogram stats",d,g)}})})];case 2:return o=s.sent(),this.logger.debug("DiagnosticsStorage: Failed to set histogram stats",o),[3,3];case 3:return[2]}})})},t.prototype.addEventRecords=function(e){return _(this,void 0,void 0,function(){var r,n,i,o,a=this;return T(this,function(s){switch(s.label){case 0:return s.trys.push([0,2,,3]),e.length===0?[2]:[4,this.getDB()];case 1:return r=s.sent(),n=r.transaction([re.EVENTS],"readwrite"),i=n.objectStore(re.EVENTS),[2,new Promise(function(l){n.oncomplete=function(){l()},n.onabort=function(c){a.logger.debug("DiagnosticsStorage: Failed to add event records",c),l()};var u=i.count();u.onsuccess=function(){var c=u.result,f=Math.max(0,fc-c);f=cn){this.logger.debug("DiagnosticsClient: Early return setTags as reaching memory limit");return}this.inMemoryTags[e]=r,this.startTimersIfNeeded()}},t.prototype.increment=function(e,r){if(r===void 0&&(r=1),!!this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryCounters).length>=cn){this.logger.debug("DiagnosticsClient: Early return increment as reaching memory limit");return}this.inMemoryCounters[e]=(this.inMemoryCounters[e]||0)+r,this.startTimersIfNeeded()}},t.prototype.recordHistogram=function(e,r){if(this.isStorageAndTrackEnabled()){if(Object.keys(this.inMemoryHistograms).length>=cn){this.logger.debug("DiagnosticsClient: Early return recordHistogram as reaching memory limit");return}var n=this.inMemoryHistograms[e];n?(n.count+=1,n.min=Math.min(n.min,r),n.max=Math.max(n.max,r),n.sum+=r):this.inMemoryHistograms[e]={count:1,min:r,max:r,sum:r},this.startTimersIfNeeded()}},t.prototype.recordEvent=function(e,r){if(this.isStorageAndTrackEnabled()){if(this.inMemoryEvents.length>=Tc){this.logger.debug("DiagnosticsClient: Early return recordEvent as reaching memory limit");return}this.inMemoryEvents.push({event_name:e,time:Date.now(),event_properties:r}),this.startTimersIfNeeded()}},t.prototype.startTimersIfNeeded=function(){var e=this;this.saveTimer||(this.saveTimer=setTimeout(function(){e.saveAllDataToStorage().catch(function(r){e.logger.debug("DiagnosticsClient: Failed to save all data to storage",r)}).finally(function(){e.saveTimer=null})},Ec)),this.flushTimer||(this.flushTimer=setTimeout(function(){e._flush().catch(function(r){e.logger.debug("DiagnosticsClient: Failed to flush",r)}).finally(function(){e.flushTimer=null})},mr))},t.prototype.saveAllDataToStorage=function(){return _(this,void 0,void 0,function(){var e,r,n,i;return T(this,function(o){switch(o.label){case 0:return this.storage?(e=C({},this.inMemoryTags),r=C({},this.inMemoryCounters),n=C({},this.inMemoryHistograms),i=$([],D(this.inMemoryEvents),!1),this.inMemoryEvents=[],this.inMemoryTags={},this.inMemoryCounters={},this.inMemoryHistograms={},[4,Promise.all([this.storage.setTags(e),this.storage.incrementCounters(r),this.storage.setHistogramStats(n),this.storage.addEventRecords(i)])]):[2];case 1:return o.sent(),[2]}})})},t.prototype._flush=function(){return _(this,void 0,void 0,function(){var e,r,n,i,o,a,s,l,u,c;return T(this,function(f){switch(f.label){case 0:return this.storage?[4,this.saveAllDataToStorage()]:[2];case 1:return f.sent(),this.saveTimer=null,this.flushTimer=null,[4,this.storage.getAllAndClear()];case 2:return e=f.sent(),r=e.tags,n=e.counters,i=e.histogramStats,o=e.events,this.storage.setLastFlushTimestamp(Date.now()),a={},r.forEach(function(d){a[d.key]=d.value}),s={},n.forEach(function(d){s[d.key]=d.value}),l={},i.forEach(function(d){l[d.key]={count:d.count,min:d.min,max:d.max,avg:Math.round(d.sum/d.count*100)/100}}),u=o.map(function(d){return{event_name:d.event_name,time:d.time,event_properties:d.event_properties}}),Object.keys(s).length===0&&Object.keys(l).length===0&&u.length===0?[2]:(c={tags:a,histogram:l,counters:s,events:u},this.fetch(c),[2])}})})},t.prototype.fetch=function(e){return _(this,void 0,void 0,function(){var r,n;return T(this,function(i){switch(i.label){case 0:if(i.trys.push([0,2,,3]),!F())throw new Error("DiagnosticsClient: Fetch is not supported");return[4,fetch(this.serverUrl,{method:"POST",headers:{"X-ApiKey":this.apiKey,"Content-Type":"application/json"},body:JSON.stringify(e)})];case 1:return r=i.sent(),r.ok?(this.logger.debug("DiagnosticsClient: Successfully sent diagnostics data"),[3,3]):(this.logger.debug("DiagnosticsClient: Failed to send diagnostics data."),[2]);case 2:return n=i.sent(),this.logger.debug("DiagnosticsClient: Failed to send diagnostics data. ",n),[3,3];case 3:return[2]}})})},t.prototype.initializeFlushInterval=function(){return _(this,void 0,void 0,function(){var e,r,n;return T(this,function(i){switch(i.label){case 0:return this.storage?(e=Date.now(),[4,this.storage.getLastFlushTimestamp()]):[2];case 1:return r=i.sent()||-1,r===-1?(this.storage.setLastFlushTimestamp(e),this._setFlushTimer(mr),[2]):(n=e-r,n>=mr?(this._flush(),[2]):(this._setFlushTimer(mr-n),[2]))}})})},t.prototype._setFlushTimer=function(e){var r=this;this.flushTimer=setTimeout(function(){r._flush().catch(function(n){r.logger.debug("DiagnosticsClient: Failed to flush",n)}).finally(function(){r.flushTimer=null})},e)},t.prototype._setSampleRate=function(e){this.logger.debug("DiagnosticsClient: Setting sample rate to",e),this.config.sampleRate=e,this.shouldTrack=ao(this.startTimestamp,this.config.sampleRate)&&this.config.enabled,this.logger.debug("DiagnosticsClient: Should track is",this.shouldTrack)},t})(),dr=(function(){function t(){}return t.prototype.send=function(e,r,n){return Promise.resolve(null)},t.prototype.buildResponse=function(e){var r,n,i,o,a,s,l,u,c,f,d,v,h,g,m,E,p,y,b,S,w,A;if(typeof e!="object")return null;var I=e.code||0,k=this.buildStatus(I);switch(k){case pe.Success:return{status:k,statusCode:I,body:{eventsIngested:(r=e.events_ingested)!==null&&r!==void 0?r:0,payloadSizeBytes:(n=e.payload_size_bytes)!==null&&n!==void 0?n:0,serverUploadTime:(i=e.server_upload_time)!==null&&i!==void 0?i:0}};case pe.Invalid:return{status:k,statusCode:I,body:{error:(o=e.error)!==null&&o!==void 0?o:"",missingField:(a=e.missing_field)!==null&&a!==void 0?a:"",eventsWithInvalidFields:(s=e.events_with_invalid_fields)!==null&&s!==void 0?s:{},eventsWithMissingFields:(l=e.events_with_missing_fields)!==null&&l!==void 0?l:{},eventsWithInvalidIdLengths:(u=e.events_with_invalid_id_lengths)!==null&&u!==void 0?u:{},epsThreshold:(c=e.eps_threshold)!==null&&c!==void 0?c:0,exceededDailyQuotaDevices:(f=e.exceeded_daily_quota_devices)!==null&&f!==void 0?f:{},silencedDevices:(d=e.silenced_devices)!==null&&d!==void 0?d:[],silencedEvents:(v=e.silenced_events)!==null&&v!==void 0?v:[],throttledDevices:(h=e.throttled_devices)!==null&&h!==void 0?h:{},throttledEvents:(g=e.throttled_events)!==null&&g!==void 0?g:[]}};case pe.PayloadTooLarge:return{status:k,statusCode:I,body:{error:(m=e.error)!==null&&m!==void 0?m:""}};case pe.RateLimit:return{status:k,statusCode:I,body:{error:(E=e.error)!==null&&E!==void 0?E:"",epsThreshold:(p=e.eps_threshold)!==null&&p!==void 0?p:0,throttledDevices:(y=e.throttled_devices)!==null&&y!==void 0?y:{},throttledUsers:(b=e.throttled_users)!==null&&b!==void 0?b:{},exceededDailyQuotaDevices:(S=e.exceeded_daily_quota_devices)!==null&&S!==void 0?S:{},exceededDailyQuotaUsers:(w=e.exceeded_daily_quota_users)!==null&&w!==void 0?w:{},throttledEvents:(A=e.throttled_events)!==null&&A!==void 0?A:[]}};case pe.Timeout:default:return{status:k,statusCode:I}}},t.prototype.buildStatus=function(e){return qn(e)?pe.Success:e===429?pe.RateLimit:e===413?pe.PayloadTooLarge:e===408?pe.Timeout:e>=400&&e<500?pe.Invalid:e>=500?pe.Failed:pe.Unknown},t})(),Ic=(function(t){Fe(e,t);function e(r){r===void 0&&(r={});var n=t.call(this)||this;return n.customHeaders=r,n}return e.prototype.send=function(r,n){return _(this,void 0,void 0,function(){var i,o,a;return T(this,function(s){switch(s.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return i={headers:C({"Content-Type":"application/json",Accept:"*/*"},this.customHeaders),body:JSON.stringify(n),method:"POST"},[4,fetch(r,i)];case 1:return o=s.sent(),[4,o.text()];case 2:a=s.sent();try{return[2,this.buildResponse(JSON.parse(a))]}catch{return[2,this.buildResponse({code:o.status})]}return[2]}})})},e})(dr),ts=2*1024;function rs(){return typeof CompressionStream<"u"}function ns(t){return _(this,void 0,void 0,function(){var e,r;return T(this,function(n){switch(n.label){case 0:if(e=CompressionStream,typeof e>"u")return[2,void 0];n.label=1;case 1:return n.trys.push([1,3,,4]),r=new Blob([t]).stream().pipeThrough(new e("gzip")),[4,new Response(r).arrayBuffer()];case 2:return[2,n.sent()];case 3:return n.sent(),[2,void 0];case 4:return[2]}})})}var Ac=(function(){function t(e,r){this.key="AMP_remote_config_".concat(e.substring(0,10)),this.logger=r}return t.prototype.fetchConfig=function(){var e=null,r={remoteConfig:null,lastFetch:new Date};try{e=localStorage.getItem(this.key)}catch(i){return this.logger.debug("Remote config localstorage failed to access: ",i),Promise.resolve(r)}if(e===null)return this.logger.debug("Remote config localstorage gets null because the key does not exist"),Promise.resolve(r);try{var n=JSON.parse(e);return this.logger.debug("Remote config localstorage parsed successfully: ".concat(JSON.stringify(n))),Promise.resolve({remoteConfig:n.remoteConfig,lastFetch:new Date(n.lastFetch)})}catch(i){return this.logger.debug("Remote config localstorage failed to parse: ",i),localStorage.removeItem(this.key),Promise.resolve(r)}},t.prototype.setConfig=function(e){try{return localStorage.setItem(this.key,JSON.stringify(e)),this.logger.debug("Remote config localstorage set successfully."),Promise.resolve(!0)}catch(r){this.logger.debug("Remote config localstorage failed to set: ",r)}return Promise.resolve(!1)},t})(),Cc="https://sr-client-cfg.amplitude.com/config",kc="https://sr-client-cfg.eu.amplitude.com/config",Pc=3,dn={INVALID_API_KEY:401,FORBIDDEN:403,RATE_LIMIT:429},Oc=1e3,Rc=300*1e3,is=(function(){function t(e,r,n,i){n===void 0&&(n="US"),this.callbackInfos=[],this.lastSuccessfulFetch=null,this.fetchPromise=null,this.isLastFetchInvalidApiKey=!1,this.apiKey=e,this.serverUrl=i||(n==="US"?Cc:kc),this.logger=r,this.storage=new Ac(e,r)}return t.prototype.subscribe=function(e,r,n){var i=Je(),o={id:i,key:e,deliveryMode:r,callback:n};return this.callbackInfos.push(o),r==="all"?this.subscribeAll(o):this.subscribeWaitForRemote(o,r.timeout),i},t.prototype.unsubscribe=function(e){var r=this.callbackInfos.findIndex(function(n){return n.id===e});return r===-1?(this.logger.debug("Remote config client unsubscribe failed because callback with id ".concat(e," doesn't exist.")),!1):(this.callbackInfos.splice(r,1),this.logger.debug("Remote config client unsubscribe succeeded removing callback with id ".concat(e,".")),!0)},t.prototype.updateConfigs=function(){return _(this,void 0,void 0,function(){var e,r,n=this;return T(this,function(i){switch(i.label){case 0:return this.lastSuccessfulFetch&&(e=Date.now()-this.lastSuccessfulFetch,e=400&&g.status<500&&g.status!==dn.RATE_LIMIT&&(d=!1),[3,6];case 4:return[4,g.json()];case 5:return E=y.sent(),[2,{value:{remoteConfig:E,lastFetch:new Date}}];case 6:return[3,9];case 7:return p=y.sent(),p instanceof Error&&p.name==="AbortError"?a.logger.debug("Remote config client fetch with retry time ".concat(e," timed out after ").concat(r,"ms")):a.logger.debug("Remote config client fetch with retry time ".concat(e," is rejected because: "),p),[3,9];case 8:return clearTimeout(h),[7];case 9:return d?f=0;a--)if(o[a]===t){o.splice(a,1);break}}catch(s){e={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(e)throw e.error}}}function Dc(){var t,e;try{for(var r=q(Object.entries(rr)),n=r.next();!n.done;n=r.next()){var i=D(n.value,2),o=i[0],a=i[1];a&&(kt[o]=a)}}catch(s){t={error:s}}finally{try{n&&!n.done&&(e=r.return)&&e.call(r)}finally{if(t)throw t.error}}rr={},yt={}}var fo={addListener:Nc,removeListener:Lc,_restoreConsole:Dc},lr;(function(t){t.US="US",t.EU="EU",t.STAGING="STAGING"})(lr||(lr={}));var Mc=null,Uc=["a","button","input","select","textarea","label","video","audio",'[contenteditable="true" i]',"[data-amp-default-track]",".amp-default-track"],os="data-amp-track-",Fc=["div","span","h1","h2","h3","h4","h5","h6"],as=150,qc=["a","button",'[role="button"]','[role="link"]','[role="menuitem"]','[role="menuitemcheckbox"]','[role="menuitemradio"]','[role="option"]','[role="tab"]','[role="treeitem"]','[contenteditable="true" i]'],ss=$(['input[type="button"]','input[type="submit"]','input[type="reset"]','input[type="image"]','input[type="file"]'],D(qc),!1),Bc=ss,Vc=ss,jc=["*"],Hc=1e3,Gc=4,Wc=50;function Kc(t){return typeof t=="string"||typeof t=="number"||typeof t=="boolean"||t===null||t===void 0}function us(t,e,r){if(t){var n=e.map(vo),i=r.map(vo);ls({json:t,allowlist:n,excludelist:i,ancestors:[]})}}function ls(t){var e,r,n=t.json,i=t.targetObject,o=t.allowlist,a=t.excludelist,s=t.ancestors,l=t.parentObject,u=t.targetKey;i||(i=n);var c=Object.keys(i);try{for(var f=q(c),d=f.next();!d.done;d=f.next()){var v=d.value,h=$($([],D(s),!1),[v],!1);Kc(i[v])?(!ho(h,o)||ho(h,a))&&delete i[v]:ls({json:n,targetObject:i[v],allowlist:o,excludelist:a,ancestors:h,parentObject:i,targetKey:v})}}catch(g){e={error:g}}finally{try{d&&!d.done&&(r=f.return)&&r.call(f)}finally{if(e)throw e.error}}Object.keys(i).length===0&&l&&u&&delete l[u]}function vo(t){return t.startsWith("/")&&(t=t.slice(1)),t.split("/").map(function(e){return e.replace(/~0/g,"~").replace(/~1/g,"/")})}function jn(t,e,r,n){if(r===void 0&&(r=0),n===void 0&&(n=0),n===e.length)return r===t.length;if(r===t.length){for(;n=e)return}}catch(m){r={error:m}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}i=u}else if(t instanceof ReadableStream){s=t;return}return i}}var Yc=(function(){function t(e){this.response=e}return t.prototype.headers=function(e){var r;if(e===void 0&&(e=[]),this.response.headers instanceof Headers){var n=this.response.headers,i={};return(r=n?.forEach)===null||r===void 0||r.call(n,function(o,a){i[a]=o}),tn(i,{allow:e})}},Object.defineProperty(t.prototype,"bodySize",{get:function(){var e,r;if(this._bodySize!==void 0)return this._bodySize;var n=(r=(e=this.response.headers)===null||e===void 0?void 0:e.get)===null||r===void 0?void 0:r.call(e,"content-length"),i=n?parseInt(n,10):void 0;return this._bodySize=i,i},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"status",{get:function(){return this.response.status},enumerable:!1,configurable:!0}),t.prototype.text=function(){return _(this,void 0,void 0,function(){var e,r,n;return T(this,function(i){switch(i.label){case 0:this.clonedResponse||(this.clonedResponse=this.response.clone()),i.label=1;case 1:return i.trys.push([1,3,,4]),e=this.clonedResponse.text(),r=new Promise(function(o){return setTimeout(function(){return o(null)},zc)}),[4,Promise.race([e,r])];case 2:return n=i.sent(),[2,n];case 3:return i.sent(),[2,null];case 4:return[2]}})})},t.prototype.json=function(e,r){return e===void 0&&(e=[]),r===void 0&&(r=[]),_(this,void 0,void 0,function(){var n;return T(this,function(i){switch(i.label){case 0:return e.length===0?[2,null]:[4,this.text()];case 1:return n=i.sent(),[2,bi(n,e,r)]}})})},t})(),Jc=(function(){function t(e,r,n,i){this.statusCode=e,this.headersString=r,this.size=n,this.getJson=i}return Object.defineProperty(t.prototype,"bodySize",{get:function(){return this.size},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"status",{get:function(){return this.statusCode},enumerable:!1,configurable:!0}),t.prototype.headers=function(e){var r,n;if(e===void 0&&(e=[]),!this.headersString)return{};var i={},o=this.headersString.split(`\r -`);try{for(var a=q(o),s=a.next();!s.done;s=a.next()){var l=s.value,u=D(l.split(": "),2),c=u[0],f=u[1];c&&f&&(i[c]=f)}}catch(d){r={error:d}}finally{try{s&&!s.done&&(n=a.return)&&n.call(a)}finally{if(r)throw r.error}}return tn(i,{allow:e})},t.prototype.json=function(e,r){return e===void 0&&(e=[]),r===void 0&&(r=[]),_(this,void 0,void 0,function(){var n;return T(this,function(i){return e.length===0?[2,null]:(n=this.getJson(),n?(us(n,e,r),[2,n]):[2,null])})})},t})();function bi(t,e,r){if(!t)return null;try{var n=JSON.parse(t);return us(n,e,r),n}catch{return null}}var nr;(function(t){t.REDACT="redact",t.REMOVE="remove"})(nr||(nr={}));var go="[REDACTED]",tn=function(t,e){var r,n,i=e.allow,o=i===void 0?[]:i,a=e.strategy,s=a===void 0?nr.REMOVE:a,l=$([],D(Il),!1),u={},c=function(h){var g=h.toLowerCase();l.find(function(m){return m.toLowerCase()===g})?s===nr.REDACT&&(u[h]=go):o.find(function(m){return m.toLowerCase()===g})?u[h]=t[h]:s===nr.REDACT&&(u[h]=go)};try{for(var f=q(Object.keys(t)),d=f.next();!d.done;d=f.next()){var v=d.value;c(v)}}catch(h){r={error:h}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}return u},Qc=(function(){function t(e,r,n,i,o,a,s,l,u,c,f){s===void 0&&(s=0),this.type=e,this.method=r,this.timestamp=n,this.startTime=i,this.url=o,this.requestWrapper=a,this.status=s,this.duration=l,this.responseWrapper=u,this.error=c,this.endTime=f}return t.prototype.toSerializable=function(){var e,r,n,i,o={type:this.type,method:this.method,url:this.url,timestamp:this.timestamp,status:this.status,duration:this.duration,error:this.error,startTime:this.startTime,endTime:this.endTime,requestHeaders:(e=this.requestWrapper)===null||e===void 0?void 0:e.headers($([],D(Wr),!1)),requestBodySize:(r=this.requestWrapper)===null||r===void 0?void 0:r.bodySize,responseHeaders:(n=this.responseWrapper)===null||n===void 0?void 0:n.headers($([],D(Wr),!1)),responseBodySize:(i=this.responseWrapper)===null||i===void 0?void 0:i.bodySize};return Object.fromEntries(Object.entries(o).filter(function(a){var s=D(a,2);s[0];var l=s[1];return l!==void 0}))},t})();function Zc(t){return typeof t=="object"&&t!==null&&"url"in t&&"method"in t}var ed=(function(){function t(e,r){r===void 0&&(r=Je()),this.callback=e,this.id=r}return t})();function ht(t){try{t()}catch{}}var td=(function(){function t(e){this.eventCallbacks=new Map,this.isObserving=!1,this.logger=e;var r=F();t.isSupported()&&(this.globalScope=r)}return t.isSupported=function(){var e=F();return!!e&&!!e.fetch},t.prototype.subscribe=function(e,r){var n,i,o,a,s,l,u,c,f,d;if(this.logger||(this.logger=r),this.eventCallbacks.set(e.id,e),!this.isObserving){var v=(o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.XMLHttpRequest)===null||i===void 0?void 0:i.prototype)===null||o===void 0?void 0:o.open,h=(l=(s=(a=this.globalScope)===null||a===void 0?void 0:a.XMLHttpRequest)===null||s===void 0?void 0:s.prototype)===null||l===void 0?void 0:l.send,g=(f=(c=(u=this.globalScope)===null||u===void 0?void 0:u.XMLHttpRequest)===null||c===void 0?void 0:c.prototype)===null||f===void 0?void 0:f.setRequestHeader;v&&h&&g&&this.observeXhr(v,h,g);var m=(d=this.globalScope)===null||d===void 0?void 0:d.fetch;m&&this.observeFetch(m),this.isObserving=!0}},t.prototype.unsubscribe=function(e){this.eventCallbacks.delete(e.id)},t.prototype.triggerEventCallbacks=function(e){var r=this;this.eventCallbacks.forEach(function(n){try{n.callback(e)}catch(i){ht(function(){var o;(o=r.logger)===null||o===void 0||o.debug("an unexpected error occurred while triggering event callbacks",i)})}})},t.prototype.handleNetworkRequestEvent=function(e,r,n,i,o,a,s){var l;if(!(a===void 0||s===void 0)){var u,c="GET";if(Zc(r)?(u=r.url,c=r.method):u=(l=r?.toString)===null||l===void 0?void 0:l.call(r),u)try{var f=new URL(u);u="".concat(f.protocol,"//").concat(f.host).concat(f.pathname).concat(f.search).concat(f.hash)}catch{}c=n?.method||c;var d,v;i&&(d=i.status),o&&(v={name:o.name||"UnknownError",message:o.message||"An unknown error occurred"},d=0);var h=Math.floor(performance.now()-s),g=Math.floor(a+h),m=new Qc(e,c,a,a,u,n,d,h,i,v,g);this.triggerEventCallbacks(m)}},t.prototype.getTimestamps=function(){var e,r;return{startTime:(e=Date.now)===null||e===void 0?void 0:e.call(Date),durationStart:(r=performance?.now)===null||r===void 0?void 0:r.call(performance)}},t.prototype.observeFetch=function(e){var r=this;!this.globalScope||!e||(this.globalScope.fetch=function(n,i){return _(r,void 0,void 0,function(){var o,a,s,l,u=this;return T(this,function(c){switch(c.label){case 0:try{o=this.getTimestamps()}catch(f){ht(function(){var d;return(d=u.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while retrieving timestamps",f)})}c.label=1;case 1:return c.trys.push([1,3,,4]),[4,e(n,i)];case 2:return a=c.sent(),[3,4];case 3:return l=c.sent(),s=l,[3,4];case 4:try{this.handleNetworkRequestEvent("fetch",n,i?new $c(i):void 0,a?new Yc(a):void 0,s,o?.startTime,o?.durationStart)}catch(f){ht(function(){var d;return(d=u.logger)===null||d===void 0?void 0:d.debug("an unexpected error occurred while handling fetch",f)})}if(a)return[2,a];throw s}})})})},t.createXhrJsonParser=function(e,r){return function(){var n;try{if(e.responseType==="json"){if(!((n=r.globalScope)===null||n===void 0)&&n.structuredClone)return r.globalScope.structuredClone(e.response)}else if(["text",""].includes(e.responseType))return JSON.parse(e.responseText)}catch(i){return i instanceof Error&&i.name==="InvalidStateError"&&ht(function(){var o;return(o=r.logger)===null||o===void 0?void 0:o.debug("unexpected error when retrieving responseText. responseType='".concat(e.responseType,"'"))}),null}return null}},t.prototype.observeXhr=function(e,r,n){if(!(!this.globalScope||!e||!r)){var i=this.globalScope.XMLHttpRequest.prototype,o=this;i.open=function(){for(var a,s=[],l=0;l"u"||!document.title)return"";var e=document.querySelector("title");return e&&e.hasAttribute(Pt)?ft:t?t(document.title):document.title};function vs(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var Gt={},mo;function od(){if(mo)return Gt;mo=1,Object.defineProperty(Gt,"__esModule",{value:!0}),Gt.Observable=void 0;const t=E=>!!Symbol[E],e=E=>t(E)?Symbol[E]:"@@"+E,r=e("iterator"),n=e("observable"),i=e("species");function o(E,p){let y=E[p];if(y!=null){if(typeof y!="function")throw new TypeError(y+" is not a function");return y}}function a(E){let p=E.constructor;return p!==void 0&&(p=p[i],p===null&&(p=void 0)),p!==void 0?p:m}function s(E){return E instanceof m}function l(E){l.log?l.log(E):setTimeout(()=>{throw E})}function u(E){Promise.resolve().then(()=>{try{E()}catch(p){l(p)}})}function c(E){let p=E._cleanup;if(p!==void 0&&(E._cleanup=void 0,!!p))try{if(typeof p=="function")p();else{let y=o(p,"unsubscribe");y&&y.call(p)}}catch(y){l(y)}}function f(E){E._observer=void 0,E._queue=void 0,E._state="closed"}function d(E){let p=E._queue;if(p){E._queue=void 0,E._state="ready";for(let y=0;yd(E));return}v(E,p,y)}}class g{constructor(p,y){this._cleanup=void 0,this._observer=p,this._queue=void 0,this._state="initializing";let b=this,S={get closed(){return b._state==="closed"},next(w){h(b,"next",w)},error(w){h(b,"error",w)},complete(){h(b,"complete")}};try{this._cleanup=y.call(void 0,S)}catch(w){S.error(w)}this._state==="initializing"&&(this._state="ready")}get closed(){return this._state==="closed"}unsubscribe(){this._state!=="closed"&&(f(this),c(this))}}class m{constructor(p){if(!(this instanceof m))throw new TypeError("Observable cannot be called as a function");if(typeof p!="function")throw new TypeError("Observable initializer must be a function");this._subscriber=p}subscribe(p){return(typeof p!="object"||p===null)&&(p={next:p,error:arguments[1],complete:arguments[2]}),new g(p,this._subscriber)}forEach(p){return new Promise((y,b)=>{if(typeof p!="function"){b(new TypeError(p+" is not a function"));return}function S(){w.unsubscribe(),y()}let w=this.subscribe({next(A){try{p(A,S)}catch(I){b(I),w.unsubscribe()}},error:b,complete:y})})}map(p){if(typeof p!="function")throw new TypeError(p+" is not a function");let y=a(this);return new y(b=>this.subscribe({next(S){try{S=p(S)}catch(w){return b.error(w)}b.next(S)},error(S){b.error(S)},complete(){b.complete()}}))}filter(p){if(typeof p!="function")throw new TypeError(p+" is not a function");let y=a(this);return new y(b=>this.subscribe({next(S){try{if(!p(S))return}catch(w){return b.error(w)}b.next(S)},error(S){b.error(S)},complete(){b.complete()}}))}reduce(p){if(typeof p!="function")throw new TypeError(p+" is not a function");let y=a(this),b=arguments.length>1,S=!1,A=arguments[1];return new y(I=>this.subscribe({next(k){let x=!S;if(S=!0,!x||b)try{A=p(A,k)}catch(P){return I.error(P)}else A=k},error(k){I.error(k)},complete(){if(!S&&!b)return I.error(new TypeError("Cannot reduce an empty sequence"));I.next(A),I.complete()}}))}async all(){let p=[];return await this.forEach(y=>p.push(y)),p}concat(...p){let y=a(this);return new y(b=>{let S,w=0;function A(I){S=I.subscribe({next(k){b.next(k)},error(k){b.error(k)},complete(){w===p.length?(S=void 0,b.complete()):A(y.from(p[w++]))}})}return A(this),()=>{S&&(S.unsubscribe(),S=void 0)}})}flatMap(p){if(typeof p!="function")throw new TypeError(p+" is not a function");let y=a(this);return new y(b=>{let S=[],w=this.subscribe({next(I){if(p)try{I=p(I)}catch(x){return b.error(x)}let k=y.from(I).subscribe({next(x){b.next(x)},error(x){b.error(x)},complete(){let x=S.indexOf(k);x>=0&&S.splice(x,1),A()}});S.push(k)},error(I){b.error(I)},complete(){A()}});function A(){w.closed&&S.length===0&&b.complete()}return()=>{S.forEach(I=>I.unsubscribe()),w.unsubscribe()}})}[n](){return this}static from(p){let y=typeof this=="function"?this:m;if(p==null)throw new TypeError(p+" is not an object");let b=o(p,n);if(b){let S=b.call(p);if(Object(S)!==S)throw new TypeError(S+" is not an object");return s(S)&&S.constructor===y?S:new y(w=>S.subscribe(w))}if(t("iterator")&&(b=o(p,r),b))return new y(S=>{u(()=>{if(!S.closed){for(let w of b.call(p))if(S.next(w),S.closed)return;S.complete()}})});if(Array.isArray(p))return new y(S=>{u(()=>{if(!S.closed){for(let w=0;w{u(()=>{if(!b.closed){for(let S=0;S0&&setTimeout(function(){u(new Error("".concat(e," timed out (id: ").concat(o,")"))),delete i.requestCallbacks[o]},n.timeout)});return s},t.prototype.handleResponse=function(e){var r;if(!this.requestCallbacks[e.id]){(r=this.logger)===null||r===void 0||r.warn("No callback found for request id: ".concat(e.id));return}this.requestCallbacks[e.id].resolve(e.responseData),delete this.requestCallbacks[e.id]},t.prototype.registerActionHandler=function(e,r){var n,i,o,a;this.actionHandlers.has(e)&&((a=(o=this.logger)===null||o===void 0?void 0:o.warn)===null||a===void 0||a.call(o,"Overwriting existing action handler for: ".concat(e))),this.actionHandlers.set(e,r);var s=this.pendingMessages.get(e);if(s){this.pendingMessages.delete(e);try{for(var l=q(s),u=l.next();!u.done;u=l.next()){var c=u.value;r(c)}}catch(f){n={error:f}}finally{try{u&&!u.done&&(i=l.return)&&i.call(l)}finally{if(n)throw n.error}}}},t.prototype.loadScriptOnce=function(e){return _(this,void 0,void 0,function(){var r,n,i;return T(this,function(o){switch(o.label){case 0:if(r=this.scriptLoadPromises.get(e),r)return[2,r];n=hd(e).then(function(){}),this.scriptLoadPromises.set(e,n),o.label=1;case 1:return o.trys.push([1,3,,4]),[4,n];case 2:return o.sent(),[3,4];case 3:throw i=o.sent(),this.scriptLoadPromises.delete(e),i;case 4:return[2]}})})},t.prototype.setup=function(e){var r=this,n,i,o=e===void 0?{}:e,a=o.logger,s=o.endpoint;a&&(this.logger=a),s&&this.endpoint===Hn&&(this.endpoint=s),!this.isSetup&&(this.isSetup=!0,(i=(n=this.logger)===null||n===void 0?void 0:n.debug)===null||i===void 0||i.call(n,"Setting up messenger"),this.messageHandler=function(l){var u,c,f,d,v;if((c=(u=r.logger)===null||u===void 0?void 0:u.debug)===null||c===void 0||c.call(u,"Message received: ",JSON.stringify(l)),r.endpoint===l.origin){var h=l.data,g=h?.action;if(g)if("id"in h&&h.id)(d=(f=r.logger)===null||f===void 0?void 0:f.debug)===null||d===void 0||d.call(f,"Received Response to previous request: ",JSON.stringify(l)),r.handleResponse(h);else{if(g==="ping"){r.notify({action:"pong"});return}var m=r.actionHandlers.get(g);if(m)m(h.data);else{var E=(v=r.pendingMessages.get(g))!==null&&v!==void 0?v:[];E.push(h.data),r.pendingMessages.set(g,E)}}}},window.addEventListener("message",this.messageHandler),this.notify({action:"page-loaded"}))},t.prototype.destroy=function(){this.messageHandler&&(window.removeEventListener("message",this.messageHandler),this.messageHandler=null),this.isSetup=!1,this.actionHandlers.clear(),this.pendingMessages.clear(),this.requestCallbacks={},this.scriptLoadPromises.clear();var e=F();e?.[$r]===this&&delete e[$r]},t})();ms=Gn;function md(t){return typeof t=="object"&&t!==null&&Gn in t&&t[Gn]===!0}function ys(t){var e=F(),r=e?.[$r];if(md(r))return r;var n=new pd(t);return e&&(e[$r]=n),n}var Eo="__AMPLITUDE_BACKGROUND_CAPTURE__";function bs(t,e){var r,n=t;if(n[Eo]!==!0){n[Eo]=!0;var i=(r=void 0)!==null&&r!==void 0?r:vd,o=null,a=function(s,l){var u,c;s==="background-capture-complete"&&((c=(u=t.logger)===null||u===void 0?void 0:u.debug)===null||c===void 0||c.call(u,"Background capture complete"),t.notify({action:"background-capture-complete",data:l}))};t.registerActionHandler("initialize-background-capture",function(){var s,l;(l=(s=t.logger)===null||s===void 0?void 0:s.debug)===null||l===void 0||l.call(s,"Initializing background capture (external script)");var u=new URL(i,t.endpoint).toString();t.loadScriptOnce(u).then(function(){var c,f,d;(f=(c=t.logger)===null||c===void 0?void 0:c.debug)===null||f===void 0||f.call(c,"Background capture script loaded (external)"),o=(d=window?.amplitudeBackgroundCapture)===null||d===void 0?void 0:d.call(window,{messenger:t,onBackgroundCapture:a}),t.notify({action:"background-capture-loaded"})}).catch(function(){var c;(c=t.logger)===null||c===void 0||c.warn("Failed to initialize background capture")})}),t.registerActionHandler("close-background-capture",function(){var s;(s=o?.close)===null||s===void 0||s.call(o),o=null})}}var gn={always:"always",ifEmptyCampaign:"ifEmptyCampaign"},Ft=function(t,e){return typeof t=="boolean"?t:t?.[e]!==!1},Es=function(t){return Ft(t,"attribution")},yd=function(t){return Ft(t,"fileDownloads")},Ss=function(t){return Ft(t,"formInteractions")},ws=function(t){return Ft(t,"pageViews")},So=function(t){return Ft(t,"sessions")},bd=function(t){return Ft(t,"pageUrlEnrichment")},Ts=function(t){return typeof t=="boolean"?t:typeof t=="object"&&(t.networkTracking===!0||typeof t.networkTracking=="object")},_s=function(t){return typeof t=="boolean"?t:typeof t=="object"&&(t.elementInteractions===!0||typeof t.elementInteractions=="object")},Ed=function(t){return typeof t=="boolean"?t:typeof t=="object"&&t.webVitals===!0},Is=function(t){return typeof t=="boolean"?t:typeof t=="object"&&(t.frustrationInteractions===!0||typeof t.frustrationInteractions=="object")},Sd=function(t){return typeof t=="boolean"?t:typeof t=="object"&&t!==null&&t.enabled!==!1},wd=function(t){if(_s(t.autocapture)&&typeof t.autocapture=="object"&&typeof t.autocapture.elementInteractions=="object")return t.autocapture.elementInteractions},Td=function(t){if(Is(t.autocapture)&&typeof t.autocapture=="object"&&typeof t.autocapture.frustrationInteractions=="object")return t.autocapture.frustrationInteractions},_d=function(t){var e;if(Ts(t.autocapture)){var r=void 0;return typeof t.autocapture=="object"&&typeof t.autocapture.networkTracking=="object"?r=t.autocapture.networkTracking:t.networkTrackingOptions&&(r=t.networkTrackingOptions),C(C({},r),{captureRules:(e=r?.captureRules)===null||e===void 0?void 0:e.map(function(n){var i,o,a;if(!((i=n.urls)===null||i===void 0)&&i.length&&(!((o=n.hosts)===null||o===void 0)&&o.length)){var s=JSON.stringify(n.hosts),l=JSON.stringify(n.urls);return(a=t.loggerProvider)===null||a===void 0||a.warn("Found network capture rule with both urls='".concat(l,"' and hosts='").concat(s,"' set. ")+"Definition of urls takes precedence over hosts, so ignoring hosts."),C(C({},n),{hosts:void 0})}return n})})}},wo=function(t){var e=function(){return!1},r=void 0,n,i=t.pageCounter,o=ws(t.defaultTracking);return o&&(e=void 0,n=void 0,t.defaultTracking&&typeof t.defaultTracking=="object"&&t.defaultTracking.pageViews&&typeof t.defaultTracking.pageViews=="object"&&("trackOn"in t.defaultTracking.pageViews&&(e=t.defaultTracking.pageViews.trackOn),"trackHistoryChanges"in t.defaultTracking.pageViews&&(r=t.defaultTracking.pageViews.trackHistoryChanges),"eventType"in t.defaultTracking.pageViews&&t.defaultTracking.pageViews.eventType&&(n=t.defaultTracking.pageViews.eventType))),{trackOn:e,trackHistoryChanges:r,eventType:n,pageCounter:i}},To=function(t){return Es(t.defaultTracking)&&t.defaultTracking&&typeof t.defaultTracking=="object"&&t.defaultTracking.attribution&&typeof t.defaultTracking.attribution=="object"?C({},t.defaultTracking.attribution):{}},Id=function(t){if(Ss(t.defaultTracking)&&t.defaultTracking&&typeof t.defaultTracking=="object"&&typeof t.defaultTracking.formInteractions=="object")return t.defaultTracking.formInteractions},pn=function(t,e){for(var r=0;r"u"&&l(new Error("XHRTransport is not supported."));var u=new XMLHttpRequest;u.open("POST",r,!0),u.onreadystatechange=function(){if(u.readyState===o.state.done){var g=u.responseText;try{s(o.buildResponse(JSON.parse(g)))}catch{s(o.buildResponse({code:u.status}))}}};var c={"Content-Type":"application/json",Accept:"*/*"},f=JSON.stringify(n),d=i&&f.length>=ts&&rs(),v=function(g){var m,E;c=C(C({},o.customHeaders),c);try{for(var p=q(Object.entries(c)),y=p.next();!y.done;y=p.next()){var b=D(y.value,2),S=b[0],w=b[1];u.setRequestHeader(S,w)}}catch(A){m={error:A}}finally{try{y&&!y.done&&(E=p.return)&&E.call(p)}finally{if(m)throw m.error}}u.send(g)},h=function(){return _(o,void 0,void 0,function(){var g;return T(this,function(m){switch(m.label){case 0:return d?[4,ns(f)]:[3,2];case 1:return g=m.sent(),g?(c["Content-Encoding"]="gzip",v(g)):v(f),[3,3];case 2:v(f),m.label=3;case 3:return[2]}})})};h().catch(l)})]})})},e})(dr),Rd=(function(t){Fe(e,t);function e(r){r===void 0&&(r={});var n=t.call(this)||this;return n.customHeaders=r,n}return e.prototype.send=function(r,n,i){return i===void 0&&(i=!1),_(this,void 0,void 0,function(){var o,a,s,l,u,c,f,d;return T(this,function(v){switch(v.label){case 0:if(typeof fetch>"u")throw new Error("FetchTransport is not supported");return o=JSON.stringify(n),a=i&&o.length>=ts&&rs(),s=o,l={"Content-Type":"application/json",Accept:"*/*"},a?[4,ns(o)]:[3,2];case 1:u=v.sent(),u&&(l["Content-Encoding"]="gzip",s=u),v.label=2;case 2:return l=C(C({},this.customHeaders),l),c={headers:l,body:s,method:"POST"},[4,fetch(r,c)];case 3:return f=v.sent(),[4,f.text()];case 4:d=v.sent();try{return[2,this.buildResponse(JSON.parse(d))]}catch{return[2,this.buildResponse({code:f.status})]}return[2]}})})},e})(dr),xd=(function(t){Fe(e,t);function e(){return t.call(this)||this}return e.prototype.send=function(r,n,i){return _(this,void 0,void 0,function(){var o=this;return T(this,function(a){return[2,new Promise(function(s,l){var u=F();if(!u?.navigator.sendBeacon)throw new Error("SendBeaconTransport is not supported");try{var c=JSON.stringify(n),f=u.navigator.sendBeacon(r,c);return s(f?o.buildResponse({code:200,events_ingested:n.events.length,payload_size_bytes:c.length,server_upload_time:Date.now()}):o.buildResponse({code:500}))}catch(d){l(d)}})]})})},e})(dr),Nd=function(t,e,r){return r===void 0&&(r=!0),_(void 0,void 0,void 0,function(){var n,i,o,a,s,l,u,c,f;return T(this,function(d){switch(d.label){case 0:return n=sc(t),[4,e.getRaw(n)];case 1:return i=d.sent(),i?r?[4,e.remove(n)]:[3,3]:[2,{optOut:!1}];case 2:d.sent(),d.label=3;case 3:return o=D(i.split("."),6),a=o[0],s=o[1],l=o[2],u=o[3],c=o[4],f=o[5],[2,{deviceId:a,userId:Ld(s),sessionId:yn(u),lastEventId:yn(f),lastEventTime:yn(c),optOut:!!l}]}})})},yn=function(t){var e=parseInt(t,32);if(!isNaN(e))return e},Ld=function(t){if(!(!atob||!escape||!t))try{return decodeURIComponent(escape(atob(t)))}catch{return}},Ge="[Amplitude]",_o="".concat(Ge," Form Started"),Dd="".concat(Ge," Form Submitted"),Md="".concat(Ge," File Downloaded"),Io="session_start",Ao="session_end",Ud="".concat(Ge," File Extension"),Fd="".concat(Ge," File Name"),qd="".concat(Ge," Link ID"),Bd="".concat(Ge," Link Text"),Vd="".concat(Ge," Link URL"),bn="".concat(Ge," Form ID"),En="".concat(Ge," Form Name"),Sn="".concat(Ge," Form Destination"),Xr="cookie",ks="US",Co=function(t){var e=t.split(".");return e.length<=2?t:e.slice(e.length-2,e.length).join(".")},jd=function(t){return Object.values(t).every(function(e){return!e})},Hd=function(t){var e=C(C({},t),{referring_domain:void 0,referrer:void 0});return Object.values(e).every(function(r){return!r})},Gd=function(t,e,r,n,i,o){i===void 0&&(i=!0),t.referrer;var a=t.referring_domain,s=Gr(t,["referrer","referring_domain"]),l=e||{};l.referrer;var u=l.referring_domain,c=Gr(l,["referrer","referring_domain"]),f=r.excludeInternalReferrers;if(f){var d=Xd(f,n);if(!(d instanceof TypeError)&&t.referring_domain&&Yd(t.referring_domain,o)){if(d==="always")return ko(d,t.referring_domain,n),!1;if(d==="ifEmptyCampaign"&&Hd(t))return ko(d,t.referring_domain,n),!1}}if(Wd(r.excludeReferrers,t.referring_domain))return n.debug("This is not a new campaign because ".concat(t.referring_domain," is in the exclude referrer list.")),!1;if(!i&&jd(t)&&e)return n.debug("This is not a new campaign because this is a direct traffic in the same session."),!1;var v=JSON.stringify(s)!==JSON.stringify(c),h=Co(a||"")!==Co(u||""),g=!e||v||h;return g?n.debug("This is a new campaign. An $identify event will be sent."):n.debug("This is not a new campaign because it's the same as the previous one."),g},Wd=function(t,e){return t===void 0&&(t=[]),e===void 0&&(e=""),t.some(function(r){return r instanceof RegExp?r.test(e):r===e})},Kd=function(t,e){var r=e.startsWith(".")?e:".".concat(e),n=t.startsWith(".")?t:".".concat(t);return!!n.endsWith(r)},zd=function(t,e){var r=C(C({},Zr),t),n=Object.entries(r).reduce(function(i,o){var a,s=D(o,2),l=s[0],u=s[1];return i.setOnce("initial_".concat(l),(a=u??e.initialEmptyValue)!==null&&a!==void 0?a:"EMPTY"),u?i.set(l,u):i.unset(l)},new tr);return pi(n)},$d=function(t){var e=t;return e?(e.startsWith(".")&&(e=e.substring(1)),[new RegExp("".concat(e.replace(".","\\."),"$"))]):[]},Xd=function(t,e){if(t===!0)return gn.always;if(typeof t=="object"){var r=t.condition;if(typeof r=="string"&&Object.keys(gn).includes(r))return r;if(typeof r>"u")return gn.always}var n="Invalid configuration provided for attribution.excludeInternalReferrers: ".concat(JSON.stringify(t));return e.error(n),new TypeError(n)};function ko(t,e,r){var n="This is not a new campaign because referring_domain=".concat(e," is on the same domain as the current page and it is configured to exclude internal referrers");t==="always"?r.debug(n):t==="ifEmptyCampaign"&&r.debug("".concat(n," with empty campaign parameters"))}var Ps=["ac.in","ac.jp","ac.kr","ac.th","ac.uk","ac.za","appspot.com","asn.au","azurewebsites.net","cloudfront.net","myshopify.com","blogspot.com","co.ca","co.in","co.jp","co.kr","co.nz","co.th","co.uk","co.za","com.ar","com.au","com.br","com.cn","com.hk","com.in","com.jp","com.kr","com.mx","com.pl","com.sg","com.tr","com.tw","ed.jp","edu.au","edu.br","edu.cn","edu.hk","edu.sg","edu.th","edu.tr","edu.tw","firebaseapp.com","fly.dev","gc.ca","geek.nz","github.io","gitlab.io","go.jp","go.kr","go.th","gob.ar","gob.mx","gov.au","gov.br","gov.cn","gov.hk","gov.in","gov.pl","gov.sg","gov.tr","gov.tw","gov.uk","gov.za","govt.nz","gr.jp","herokuapp.com","id.au","idv.hk","iwi.nz","lg.jp","ltd.uk","maori.nz","me.uk","mil.kr","ne.jp","ne.kr","net.au","net.br","net.cn","net.hk","net.in","net.nz","net.pl","net.sg","net.tr","net.tw","net.za","onrender.com","or.jp","or.kr","or.th","org.ar","org.au","org.br","org.cn","org.hk","org.in","org.mx","org.nz","org.pl","org.sg","org.tw","org.uk","org.za","pages.dev","pe.kr","plc.uk","re.kr","res.in","sch.uk","vercel.app","netlify.app","workers.dev"],Os=function(t){var e,r,n=t||((r=(e=F())===null||e===void 0?void 0:e.location)===null||r===void 0?void 0:r.hostname);if(!n)return"";var i=n.split("."),o=i[i.length-1],a=i[i.length-2];return Ps.find(function(s){return n.endsWith(".".concat(s))})&&(o=i[i.length-2]+"."+i[i.length-1],a=i[i.length-3]),a?"".concat(a,".").concat(o):o},Yd=function(t,e){var r=F();if(!r)return!1;var n=(e||"").trim()||Os(r.location.hostname);return Kd(t,n)},Jd=(function(t){Fe(e,t);function e(r,n,i,o,a,s,l,u,c,f,d,v,h,g,m,E,p,y,b,S,w,A,I,k,x,P,R,O,L,B,H,j,ee,M,G,W,te,J,Z,K,ie,oe,fe,ve,me){i===void 0&&(i=new yi),o===void 0&&(o={domain:"",expiration:365,sameSite:"Lax",secure:!1,upgrade:!0}),u===void 0&&(u=1e3),c===void 0&&(c=5),f===void 0&&(f=30),d===void 0&&(d=Xr),E===void 0&&(E=new Dt),p===void 0&&(p=ke.Warn),b===void 0&&(b=!1),S===void 0&&(S=!1),I===void 0&&(I=""),k===void 0&&(k=ks),R===void 0&&(R=1800*1e3),O===void 0&&(O=new Cs({loggerProvider:E})),L===void 0&&(L={ipAddress:!0,language:!0,platform:!0}),B===void 0&&(B="fetch"),H===void 0&&(H=!1),j===void 0&&(j=!0),J===void 0&&(J=!0),Z===void 0&&(Z=0),fe===void 0&&(fe=!1),ve===void 0&&(ve=!1);var N=this,he;N=t.call(this,{apiKey:r,storageProvider:O,transportProvider:Rs(B)})||this,N.apiKey=r,N.appVersion=n,N.cookieOptions=o,N.defaultTracking=a,N.autocapture=s,N.flushIntervalMillis=u,N.flushMaxRetries=c,N.flushQueueSize=f,N.identityStorage=d,N.ingestionMetadata=v,N.instanceName=h,N.loggerProvider=E,N.logLevel=p,N.minIdLength=y,N.offline=b,N.partnerId=w,N.plan=A,N.serverUrl=I,N.serverZone=k,N.sessionTimeout=R,N.storageProvider=O,N.trackingOptions=L,N.transport=B,N.useBatch=H,N.fetchRemoteConfig=j,N.networkTrackingOptions=W,N.identify=te,N.enableDiagnostics=J,N.diagnosticsSampleRate=Z,N.diagnosticsClient=K,N.remoteConfig=ie,N.topLevelDomain=oe,N.enableRequestBodyCompression=fe,N._enableRequestBodyCompressionExperimental=ve,N.customEnrichment=me,N.version=Ti,N._optOut=!1,N._cookieStorage=i,N.deviceId=l,N.lastEventId=g,N.lastEventTime=m,N.optOut=S,N.deferredSessionId=P,N.sessionId=x,N.pageCounter=M,N.userId=ee,N.debugLogsEnabled=G,N.loggerProvider.enable(G?ke.Debug:N.logLevel),N.networkTrackingOptions=W,N.identify=te,N.enableDiagnostics=J,N.diagnosticsSampleRate=Z,N.diagnosticsClient=K;var qe=(he=ie?.fetchRemoteConfig)!==null&&he!==void 0?he:j;return N.remoteConfig=N.remoteConfig||{},N.remoteConfig.fetchRemoteConfig=qe,N.fetchRemoteConfig=qe,N.topLevelDomain=oe||Os(),N}return Object.defineProperty(e.prototype,"cookieStorage",{get:function(){return this._cookieStorage},set:function(r){this._cookieStorage!==r&&(this._cookieStorage=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"deviceId",{get:function(){return this._deviceId},set:function(r){this._deviceId!==r&&(this._deviceId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"userId",{get:function(){return this._userId},set:function(r){this._userId!==r&&(this._userId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"sessionId",{get:function(){return this._sessionId},set:function(r){this._sessionId!==r&&(this._sessionId=r,r!==void 0&&this._deferredSessionId!==void 0&&(this._deferredSessionId=void 0),this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"deferredSessionId",{get:function(){return this._deferredSessionId},set:function(r){this._deferredSessionId!==r&&r!==this.sessionId&&(this._deferredSessionId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"optOut",{get:function(){return this._optOut},set:function(r){this._optOut!==r&&(this._optOut=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"lastEventTime",{get:function(){return this._lastEventTime},set:function(r){this._lastEventTime!==r&&(this._lastEventTime=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"lastEventId",{get:function(){return this._lastEventId},set:function(r){this._lastEventId!==r&&(this._lastEventId=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"pageCounter",{get:function(){return this._pageCounter},set:function(r){this._pageCounter!==r&&(this._pageCounter=r,this.updateStorage())},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"debugLogsEnabled",{set:function(r){this._debugLogsEnabled!==r&&(this._debugLogsEnabled=r,this.updateStorage())},enumerable:!1,configurable:!0}),e.prototype.updateStorage=function(){var r={deviceId:this._deviceId,userId:this._userId,sessionId:this._sessionId,deferredSessionId:this._deferredSessionId,optOut:this._optOut,lastEventTime:this._lastEventTime,lastEventId:this._lastEventId,pageCounter:this._pageCounter,debugLogsEnabled:this._debugLogsEnabled,cookieDomain:void 0};this.cookieStorage instanceof zr&&(r.cookieDomain=this.cookieStorage.options.domain),this.cookieStorage.set(Qa(this.apiKey),r)},e})(Xa),Qd=function(t,e,r,n,i){return e===void 0&&(e={}),_(void 0,void 0,void 0,function(){var o,a,s,l,u,c,f,d,v,h,g,m,E,p,y,b,S,w,A,I,k,x,P,R,O,L,B,H,j,ee,M,G,W,te,J,Z,K,ie,oe,fe,ve,me,N,he,qe,Ae,Re,Be,Ve,We,Ke;return T(this,function(ge){switch(ge.label){case 0:return o=e.identityStorage||Xr,a="",o===Xr&&!(!((x=e.cookieOptions)===null||x===void 0)&&x.domain)&&((P=e.cookieOptions)===null||P===void 0?void 0:P.domain)!==""?[4,tf(void 0,n)]:[3,2];case 1:a=ge.sent(),ge.label=2;case 2:return s=C({domain:(O=(R=e.cookieOptions)===null||R===void 0?void 0:R.domain)!==null&&O!==void 0?O:a,expiration:365,sameSite:"Lax",secure:!1,upgrade:!0},e.cookieOptions),l={duplicateResolverFn:function(Ne){var nt=Za(Ne);if(!nt)return!1;var V=JSON.parse(nt);return es(V.cookieDomain,s.domain)},diagnosticsClient:n},u=Zd(e.identityStorage,s,l),[4,Nd(t,u,(B=(L=e.cookieOptions)===null||L===void 0?void 0:L.upgrade)!==null&&B!==void 0?B:!0)];case 3:return c=ge.sent(),[4,u.get(Qa(t))];case 4:return f=ge.sent(),d=Kr(),v=d.ampTimestamp?Number(d.ampTimestamp):void 0,h=v?Date.now()"u"||!location.hostname)return[2,""];if(r=location.hostname,n=r.split("."),n.length===1)return[2,""];for(i=[],o=1,Ps.find(function(f){return r.endsWith(".".concat(f))})&&(o=2),a=n.length-o-1;a>=0;--a)i.push(n.slice(a).join("."));a=0,c.label=2;case 2:if(!(a"u"||typeof s=="function"&&s()},h=typeof location<"u"?location.href:null,g=function(){return _(void 0,void 0,void 0,function(){var p,y,b,S,w;return T(this,function(A){switch(A.label){case 0:return p=location.href,y=sf(l,p,h||"")&&v(),h=p,y?(b=void 0,a&&(b=Je(),a.set(wn,{pageViewId:b})),n?.log("Tracking page view event"),e!=null?[3,1]:[3,3]):[3,4];case 1:return w=(S=e).track,[4,d(b)];case 2:w.apply(S,[A.sent()]),A.label=3;case 3:A.label=4;case 4:return[2]}})})},m=function(){g()},E={name:"@amplitude/plugin-page-view-tracking-browser",type:"enrichment",setup:function(p,y){return _(void 0,void 0,void 0,function(){var b,S,w;return T(this,function(A){switch(A.label){case 0:if(e=y,o=p,n=p.loggerProvider,n.log("Installing @amplitude/plugin-page-view-tracking-browser"),i=!0,r){try{a=new en(r.sessionStorage)}catch{n?.debug("sessionStorage is not available in this environment.")}r.addEventListener("popstate",m),r.history.pushState=new Proxy(r.history.pushState,{apply:function(I,k,x){var P=D(x,3),R=P[0],O=P[1],L=P[2];I.apply(k,[R,O,L]),i&&m()}})}return v()?(n.log("Tracking page view event"),b=void 0,a&&(b=Je(),a.set(wn,{pageViewId:b})),w=(S=e).track,[4,d(b)]):[3,2];case 1:w.apply(S,[A.sent()]),A.label=2;case 2:return[2]}})})},execute:function(p){return _(void 0,void 0,void 0,function(){var y,b,S;return T(this,function(w){switch(w.label){case 0:return s==="attribution"&&af(p)?(n?.log("Enriching campaign event to page view event with campaign parameters"),y=void 0,a?[4,a.get(wn)]:[3,2]):[3,4];case 1:b=w.sent(),y=b?.pageViewId,w.label=2;case 2:return[4,d(y)];case 3:S=w.sent(),p.event_type=S.event_type,p.event_properties=C(C({},p.event_properties),S.event_properties),w.label=4;case 4:return o&&p.event_type===c&&(o.pageCounter=o.pageCounter?o.pageCounter+1:1,p.event_properties=C(C({},p.event_properties),{"[Amplitude] Page Counter":o.pageCounter})),[2,p]}})})},teardown:function(){return _(void 0,void 0,void 0,function(){return T(this,function(p){return r&&(r.removeEventListener("popstate",m),i=!1),[2]})})}};return E},of=function(){return _(void 0,void 0,void 0,function(){var t;return T(this,function(e){switch(e.label){case 0:return t=rf,[4,new fs().parse()];case 1:return[2,t.apply(void 0,[e.sent()])]}})})},af=function(t){if(t.event_type==="$identify"&&t.user_properties){var e=t.user_properties,r=e[ct.SET]||{},n=e[ct.UNSET]||{},i=$($([],D(Object.keys(r)),!1),D(Object.keys(n)),!1);return Object.keys(Zr).every(function(o){return i.includes(o)})}return!1},sf=function(t,e,r){switch(t){case"pathOnly":{if(r=="")return!0;var n=new URL(e),i=new URL(r),o=n.origin+n.pathname,a=i.origin+i.pathname;return o!==a}default:return e!==r}},uf=function(){var t,e=[],r=function(c,f,d){c.addEventListener(f,d),e.push({element:c,type:f,handler:d})},n=function(){e.forEach(function(c){var f=c.element,d=c.type,v=c.handler;f?.removeEventListener(d,v)}),e=[]},i,o="@amplitude/plugin-form-interaction-tracking-browser",a="enrichment",s=function(c,f){return _(void 0,void 0,void 0,function(){var d,v;return T(this,function(h){return i=Id(c),d=function(){if(!f){c.loggerProvider.warn("Form interaction tracking requires a later version of @amplitude/analytics-browser. Form interaction events are not tracked.");return}if(!(typeof document>"u")){var g=new WeakSet,m=function(p){if(!g.has(p)){g.add(p);var y=!1;r(p,"change",function(){var b,S=Oo(p);y||f.track(_o,(b={},b[bn]=It(p.id),b[En]=It(p.name),b[Sn]=S,b)),y=!0}),r(p,"submit",function(b){var S,w,A=Oo(p);if(y||f.track(_o,(S={},S[bn]=It(p.id),S[En]=It(p.name),S[Sn]=A,S)),y=!0,i?.shouldTrackSubmit!==void 0)if(typeof i.shouldTrackSubmit=="function"&&typeof SubmitEvent<"u"&&b instanceof SubmitEvent)try{var I=i.shouldTrackSubmit(b);if(!I)return}catch{c.loggerProvider.warn("shouldTrackSubmit callback threw an error, proceeding with tracking.")}else c.loggerProvider.warn("shouldTrackSubmit is ignored because it is not a function or event is not a SubmitEvent.");f.track(Dd,(w={},w[bn]=It(p.id),w[En]=It(p.name),w[Sn]=A,w)),y=!1})}},E=Array.from(document.getElementsByTagName("form"));E.forEach(m),typeof MutationObserver<"u"&&(t=new MutationObserver(function(p){p.forEach(function(y){y.addedNodes.forEach(function(b){b.nodeName==="FORM"&&m(b),"querySelectorAll"in b&&typeof b.querySelectorAll=="function"&&Array.from(b.querySelectorAll("form")).map(m)})})}),t.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?d():(v=F(),v?v.addEventListener("load",d):c.loggerProvider.debug("Form interaction tracking is not installed because global is undefined.")),[2]})})},l=function(c){return _(void 0,void 0,void 0,function(){return T(this,function(f){return[2,c]})})},u=function(){return _(void 0,void 0,void 0,function(){return T(this,function(c){return t?.disconnect(),n(),[2]})})};return{name:o,type:a,setup:s,execute:l,teardown:u}},It=function(t){if(typeof t=="string")return t},Oo=function(t){var e=t.getAttribute("action");try{e=new URL(encodeURI(e??""),window.location.href).href}catch{}return e},lf=function(){var t,e=[],r=function(u,c,f){u.addEventListener(c,f),e.push({element:u,type:c,handler:f})},n=function(){e.forEach(function(u){var c=u.element,f=u.type,d=u.handler;c?.removeEventListener(f,d)}),e=[]},i="@amplitude/plugin-file-download-tracking-browser",o="enrichment",a=function(u,c){return _(void 0,void 0,void 0,function(){var f,d;return T(this,function(v){return f=function(){if(!c){u.loggerProvider.warn("File download tracking requires a later version of @amplitude/analytics-browser. File download events are not tracked.");return}if(!(typeof document>"u")){var h=function(E){var p;try{p=new URL(E.href,window.location.href)}catch{return}var y=g.exec(p.href),b=y?.[1];b&&r(E,"click",function(){var S;b&&c.track(Md,(S={},S[Ud]=b,S[Fd]=p.pathname,S[qd]=E.id,S[Bd]=E.text,S[Vd]=E.href,S))})},g=/\.(pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma)(\?.+)?$/,m=Array.from(document.getElementsByTagName("a"));m.forEach(h),typeof MutationObserver<"u"&&(t=new MutationObserver(function(E){E.forEach(function(p){p.addedNodes.forEach(function(y){y.nodeName==="A"&&h(y),"querySelectorAll"in y&&typeof y.querySelectorAll=="function"&&Array.from(y.querySelectorAll("a")).map(h)})})}),t.observe(document.body,{subtree:!0,childList:!0}))}},document.readyState==="complete"?f():(d=F(),d?d.addEventListener("load",f):u.loggerProvider.debug("File download tracking is not installed because global is undefined.")),[2]})})},s=function(u){return _(void 0,void 0,void 0,function(){return T(this,function(c){return[2,u]})})},l=function(){return _(void 0,void 0,void 0,function(){return T(this,function(u){return t?.disconnect(),n(),[2]})})};return{name:i,type:o,setup:a,execute:s,teardown:l}},Ro=!1,cf=function(t){if(!(Ro||t.defaultTracking!==void 0)){var e=`\`options.defaultTracking\` is set to undefined. This implicitly configures your Amplitude instance to track Page Views, Sessions, File Downloads, and Form Interactions. You can suppress this warning by explicitly setting a value to \`options.defaultTracking\`. The value must either be a boolean, to enable and disable all default events, or an object, for advanced configuration. For example: - -amplitude.init(, { - defaultTracking: true, -}); - -Visit https://www.docs.developers.amplitude.com/data/sdks/browser-2/#tracking-default-events for more details.`;t.loggerProvider.warn(e),Ro=!0}},df=function(){var t="@amplitude/plugin-network-checker-browser",e="before",r=F(),n=[],i=function(l,u){r?.addEventListener&&(r?.addEventListener(l,u),n.push({type:l,handler:u}))},o=function(){n.forEach(function(l){var u=l.type,c=l.handler;r?.removeEventListener(u,c)}),n=[]},a=function(l,u){return _(void 0,void 0,void 0,function(){return T(this,function(c){return typeof navigator>"u"?(l.loggerProvider.debug("Network connectivity checker plugin is disabled because navigator is not available."),l.offline=!1,[2]):(l.offline=!navigator.onLine,i("online",function(){l.loggerProvider.debug("Network connectivity changed to online."),l.offline=!1,setTimeout(function(){u.flush()},l.flushIntervalMillis)}),i("offline",function(){l.loggerProvider.debug("Network connectivity changed to offline."),l.offline=!0}),[2])})})},s=function(){return _(void 0,void 0,void 0,function(){return T(this,function(l){return o(),[2]})})};return{name:t,type:e,setup:a,teardown:s}};function xs(t){var e,r,n,i,o,a,s,l,u,c,f,d;if(!(typeof t!="object"||t===null)&&!Array.isArray(t)){var v=Object.keys(t);try{for(var h=q(v),g=h.next();!g.done;g=h.next()){var m=g.value;try{var E=t[m];typeof E?.enabled=="boolean"&&(E.enabled?(delete E.enabled,Object.keys(E).length===0&&(t[m]=!0)):t[m]=!1),xs(E)}catch{}}}catch(L){e={error:L}}finally{try{g&&!g.done&&(r=h.return)&&r.call(h)}finally{if(e)throw e.error}}try{if(!((u=(l=(s=t.autocapture)===null||s===void 0?void 0:s.networkTracking)===null||l===void 0?void 0:l.captureRules)===null||u===void 0)&&u.length)try{for(var p=q(t.autocapture.networkTracking.captureRules),y=p.next();!y.done;y=p.next()){var b=y.value;try{for(var S=(o=void 0,q(["responseHeaders","requestHeaders"])),w=S.next();!w.done;w=S.next()){var A=w.value,I=(c=b[A])!==null&&c!==void 0?c:{},k=I.captureSafeHeaders,x=I.allowlist;if(!(!k&&!x)){if(x!==void 0&&!Array.isArray(x)){delete b[A];continue}b[A]=$($([],D(k?Wr:[]),!1),D(x??[]),!1)}}}catch(L){o={error:L}}finally{try{w&&!w.done&&(a=S.return)&&a.call(S)}finally{if(o)throw o.error}}}}catch(L){n={error:L}}finally{try{y&&!y.done&&(i=p.return)&&i.call(p)}finally{if(n)throw n.error}}}catch{}var P=(f=t.autocapture)===null||f===void 0?void 0:f.frustrationInteractions;P&&(P.rageClick&&(P.rageClicks=P.rageClick,delete P.rageClick),P.deadClick&&(P.deadClicks=P.deadClick,delete P.deadClick));try{var R=(d=t.autocapture)===null||d===void 0?void 0:d.elementInteractions;if(R&&typeof R=="object"&&(R.viewportContentUpdated===!0&&(R.viewportContentUpdated={}),R.viewportContentUpdated===!1&&(R.viewportContentUpdated={enabled:!1}),R.exposureDuration!==void 0)){var O=R.viewportContentUpdated;O===void 0?R.viewportContentUpdated={exposureDuration:R.exposureDuration}:typeof O=="object"&&O.exposureDuration===void 0&&O.enabled!==!1&&(O.exposureDuration=R.exposureDuration),delete R.exposureDuration}}catch{}}}function xo(t,e,r){var n,i,o=[];try{for(var a=q(e??[]),s=a.next();!s.done;s=a.next()){var l=s.value;try{o.push(new RegExp(l))}catch(u){r.loggerProvider.warn("Invalid regex pattern: ".concat(l),u)}}}catch(u){n={error:u}}finally{try{s&&!s.done&&(i=a.return)&&i.call(a)}finally{if(n)throw n.error}}return t.concat(o)}function ff(t,e){var r,n,i,o,a,s,l;if(t){xs(t);try{e.loggerProvider.debug("Update browser config with remote configuration:",JSON.stringify(t));var u=t;if(u&&"autocapture"in u){if(typeof u.autocapture=="boolean"&&(e.autocapture=u.autocapture),typeof u.autocapture=="object"&&u.autocapture!==null){var c=C({},u.autocapture);if(e.autocapture===void 0&&(e.autocapture=u.autocapture),typeof u.autocapture.elementInteractions=="object"&&u.autocapture.elementInteractions!==null&&(!((i=u.autocapture.elementInteractions.pageUrlAllowlistRegex)===null||i===void 0)&&i.length)){c.elementInteractions=C({},u.autocapture.elementInteractions);var f=c.elementInteractions,d=(o=f.pageUrlAllowlist)!==null&&o!==void 0?o:[],v=u.autocapture.elementInteractions.pageUrlAllowlistRegex;f.pageUrlAllowlist=xo(d,v,e),delete f.pageUrlAllowlistRegex}if(typeof u.autocapture.networkTracking=="object"&&u.autocapture.networkTracking!==null&&(!((a=u.autocapture.networkTracking.captureRules)===null||a===void 0)&&a.length)){c.networkTracking=C({},u.autocapture.networkTracking);var h=c.networkTracking,g=(s=h.captureRules)!==null&&s!==void 0?s:[];try{for(var m=q(g),E=m.next();!E.done;E=m.next()){var p=E.value;p.urls=xo((l=p.urls)!==null&&l!==void 0?l:[],p.urlsRegex,e),delete p.urlsRegex}}catch(y){r={error:y}}finally{try{E&&!E.done&&(n=m.return)&&n.call(m)}finally{if(r)throw r.error}}}typeof e.autocapture=="boolean"&&(e.autocapture=C({attribution:e.autocapture,fileDownloads:e.autocapture,formInteractions:e.autocapture,pageViews:e.autocapture,sessions:e.autocapture,elementInteractions:e.autocapture,webVitals:e.autocapture,frustrationInteractions:e.autocapture},c)),typeof e.autocapture=="object"&&(e.autocapture=C(C({},e.autocapture),c))}e.defaultTracking=e.autocapture}"customEnrichment"in u&&u.customEnrichment!==null&&(e.customEnrichment=u.customEnrichment),e.loggerProvider.debug("Browser config after remote config update:",JSON.stringify(e))}catch(y){e.loggerProvider.error("Failed to apply remote configuration because of error: ",y)}}}var Ns="1.25.1",vf="@amplitude/plugin-autocapture-browser",hf="@amplitude/plugin-frustration-browser",Ls="[Amplitude] Element Clicked",gf="[Amplitude] Dead Click",pf="[Amplitude] Rage Click",mf="[Amplitude] Error Click",yf="[Amplitude] Element Changed",bf="[Amplitude] Thrashed Cursor",Ef="[Amplitude] Element ID",Sf="[Amplitude] Element Class",No="[Amplitude] Element Tag",Lo="[Amplitude] Element Text",wf="[Amplitude] Element Hierarchy",Tf="[Amplitude] Element Href",_f="[Amplitude] Element Position Left",If="[Amplitude] Element Position Top",Af="[Amplitude] Element Aria Label",Cf="[Amplitude] Element Attributes",kf="[Amplitude] Element Path",Pf="[Amplitude] Element Parent Label",Wn="[Amplitude] Page URL",Of="[Amplitude] Page Title",Ds="[Amplitude] Viewport Height",Ms="[Amplitude] Viewport Width",Rf="[Amplitude] Max Page X",xf="[Amplitude] Max Page Y",Us="[Amplitude] Page View ID",Nf="https://cdn.amplitude.com/libs/visual-tagging-selector-1.0.0-alpha.js.gz",Lf="amp-visual-tagging-selector-highlight",Fs="data-amp-mask-attributes",Df=25,qs=128,Mf="AMP_PAGE_VIEW",Uf=18e3,Ff=["input","select","textarea"],Bs=function(t,e){var r,n=(r=window?.getComputedStyle)===null||r===void 0?void 0:r.call(window,t);return n?.getPropertyValue("cursor")==="pointer"&&e==="click"},Vs=function(t){var e=t.pageUrlAllowlist,r=t.pageUrlExcludelist;return!(r&&r.length>0&&Bn(window.location.href,r)||!Bn(window.location.href,e))},Nt=function(t,e,r){return r===void 0&&(r=!1),function(n,i){var o,a,s=t.shouldTrackEventResolver,l=(a=(o=i?.tagName)===null||o===void 0?void 0:o.toLowerCase)===null||a===void 0?void 0:a.call(o);if(!l)return!1;if(s)return s(n,i);if(!Vs(t))return!1;var u=String(i?.getAttribute("type"))||"";if(typeof u=="string")switch(u.toLowerCase()){case"hidden":return!1;case"password":return!1}var c=Bs(i,n);if(r&&c)return!0;if(e){var f=e.some(function(d){var v;return!!(!((v=i?.matches)===null||v===void 0)&&v.call(i,d))});if(!f)return!1}switch(l){case"input":case"select":case"textarea":return n==="change"||n==="click";default:return c?!0:n==="click"}}},qf=function(t){var e,r,n,i=(r=(e=t?.tagName)===null||e===void 0?void 0:e.toLowerCase)===null||r===void 0?void 0:r.call(e),o=t instanceof HTMLElement?((n=t.getAttribute("contenteditable"))===null||n===void 0?void 0:n.toLowerCase())==="true":!1;return!Ff.includes(i)&&!o},Bf=function(t){return t?t.split(",").map(function(e){return e.trim()}).filter(function(e){return e.length>0&&e!=="id"&&e!=="class"}):[]},Vf=function(t,e){return Object.entries(t).reduce(function(r,n){var i=D(n,2),o=i[0],a=i[1];if(o.startsWith(e)){var s=o.replace(e,"");s&&(r[s]=a||"")}return r},{})},jf=function(t){return t==null||typeof t=="object"&&Object.keys(t).length===0||typeof t=="string"&&t.trim().length===0},Do=function(t){return Object.keys(t).reduce(function(e,r){var n=t[r];return jf(n)||(e[r]=n),e},{})},js=function(){var t;try{var e=F(),r=(t=e?.sessionStorage)===null||t===void 0?void 0:t.getItem(Mf);if(!r)return;var n=JSON.parse(r);if(typeof n.pageViewId=="string")return n.pageViewId}catch{}},_i=function(t,e){return t?e.some(function(r){var n;return(n=t?.matches)===null||n===void 0?void 0:n.call(t,r)})?t:_i(t?.parentElement,e):null},fr=function(t){return!(t.event.target===null||!t.closestTrackedAncestor)};function Hf(t){return t.type==="click"||t.type==="change"}var Yr;(function(t){t[t.LEFT_OR_TOUCH_CONTACT=0]="LEFT_OR_TOUCH_CONTACT",t[t.MIDDLE=1]="MIDDLE",t[t.RIGHT=2]="RIGHT"})(Yr||(Yr={}));var Mo="__AMPLITUDE_VISUAL_TAGGING__";function Gf(t,e){var r=t;if(r[Mo]!==!0){r[Mo]=!0;var n=e.dataExtractor,i=e.isElementSelectable,o=e.cssSelectorAllowlist,a=e.actionClickAllowlist,s=null,l=function(c){t.notify({action:"element-selected",data:c})},u=function(c,f){c==="selector-mode-changed"?t.notify({action:"track-selector-mode-changed",data:f}):c==="selector-moved"&&t.notify({action:"track-selector-moved",data:f})};t.registerActionHandler("initialize-visual-tagging-selector",function(c){t.loadScriptOnce(Nf).then(function(){var f;s=(f=window?.amplitudeVisualTaggingSelector)===null||f===void 0?void 0:f.call(window,{getEventTagProps:n.getEventTagProps,isElementSelectable:function(d){return i?i(c?.actionType||"click",d):!0},onTrack:u,onSelect:l,visualHighlightClass:Lf,messenger:t,cssSelectorAllowlist:o,actionClickAllowlist:a,extractDataFromDataSource:n.extractDataFromDataSource,dataExtractor:n,diagnostics:{autocapture:{version:Ns}}}),t.notify({action:"selector-loaded"})}).catch(function(){var f;(f=t.logger)===null||f===void 0||f.warn("Failed to initialize visual tagging selector")})}),t.registerActionHandler("close-visual-tagging-selector",function(){var c;(c=s?.close)===null||c===void 0||c.call(s)})}}function Wf(t){var e=t.amplitude,r=t.allObservables,n=t.shouldTrackEvent,i=t.evaluateTriggers,o=r.clickObservable,a=o.filter(fr).filter(function(l){return n("click",l.closestTrackedAncestor)}).map(function(l){return i(l)}),s=a;return s.subscribe(function(l){e?.track(Ls,l.targetElementProperties)})}function Kf(t){var e=t.amplitude,r=t.allObservables,n=t.getEventProperties,i=t.shouldTrackEvent,o=t.evaluateTriggers,a=r.changeObservable,s=a.filter(fr).filter(function(l){return i("change",l.closestTrackedAncestor)}).map(function(l){return o(l)});return s.subscribe(function(l){e?.track(yf,n("change",l.closestTrackedAncestor))})}function zf(t){var e=t.amplitude,r=t.allObservables,n=t.options,i=t.getEventProperties,o=t.shouldTrackEvent,a=t.shouldTrackActionClick,s=r.clickObservable,l=r.mutationObservable,u=r.navigateObservable,c=s.filter(function(m){return!o("click",m.closestTrackedAncestor)}).map(function(m){var E=_i(m.event.target,n.actionClickAllowlist);return m.closestTrackedAncestor=E,m.closestTrackedAncestor!==null&&(m.targetElementProperties=i(m.type,m.closestTrackedAncestor)),m}).filter(fr).filter(function(m){return a("click",m.closestTrackedAncestor)}),f=u?Et(l,u):l,d=Et(c,f),v=null,h=null,g=wi(d,function(m){if(v&&(clearTimeout(v),v=null),m.type==="click")return h=m,v=setTimeout(function(){v=null,h=null},500),Promise.resolve(null);if(h){var E=h;return h=null,Promise.resolve(E)}return Promise.resolve(null)});return g.subscribe(function(m){m&&e?.track(Ls,i("click",m.closestTrackedAncestor))})}function $f(t){var e=t.allObservables,r=e.scrollObservable,n={maxX:0,maxY:0},i=r.subscribe(function(){var o,a,s,l,u=F(),c=Math.floor((a=(o=u?.scrollX)!==null&&o!==void 0?o:u?.pageXOffset)!==null&&a!==void 0?a:0),f=Math.floor((l=(s=u?.scrollY)!==null&&s!==void 0?s:u?.pageYOffset)!==null&&l!==void 0?l:0);n.maxX=Math.max(n.maxX,c),n.maxY=Math.max(n.maxY,f)});return{unsubscribe:function(){i.unsubscribe()},getState:function(){return n},reset:function(){n.maxX=0,n.maxY=0}}}var Ut=F(),Hs=function(){return new we(function(t){var e=new MutationObserver(function(r){t.next(r)});return document.body&&e.observe(document.body,{childList:!0,attributes:!0,characterData:!0,subtree:!0}),function(){return e.disconnect()}})},Gs=function(t){return t===void 0&&(t="click"),new we(function(e){var r,n=function(i){e.next(i)};return(r=F())===null||r===void 0||r.document.addEventListener(t,n,{capture:!0}),function(){var i;(i=F())===null||i===void 0||i.document.removeEventListener(t,n,{capture:!0})}})},Xf=function(){return new we(function(t){var e,r=function(n){t.next(n)};return(e=F())===null||e===void 0||e.addEventListener("scroll",r),function(){var n;(n=F())===null||n===void 0||n.removeEventListener("scroll",r)}})},Yf=function(){return new we(function(t){var e=function(r){for(var n=[],i=1;i ")},pv=function(t,e,r){var n,i;if(t.nodeType!==Node.ELEMENT_NODE)return null;var o=t.getAttribute("id");if(e){if(o)return new Wt(c(o),!0);var a=t.tagName.toLowerCase();if(a==="body"||a==="head"||a==="html")return new Wt(a,!0)}var s=t.tagName.toLowerCase();if(o)return new Wt(s+c(o),!0);var l=t.parentNode;if(!l||l.nodeType===Node.DOCUMENT_NODE)return new Wt(s,!0);function u(P){var R=P.getAttribute("class");return R?R.split(/\s+/g).filter(Boolean).map(function(O){return"$"+O}):[]}function c(P){return"#"+CSS.escape(P)}for(var f=u(t),d=!1,v=!1,h=-1,g=-1,m=l.children,E=0;m&&(h===-1||!v)&&E=0;S--){var w=y[S];if(w){var A=Bf(w.getAttribute(Fs)),I=S===y.length-1?[]:(g=b.get(y[S+1]))!==null&&g!==void 0?g:new Set,k=new Set($($([],D(I),!1),D(A),!1));b.set(w,k)}}p=y.map(function(B){var H;return vv(B,(H=b.get(B))!==null&&H!==void 0?H:new Set)});var x=function(B){B?.attrs&&Object.entries(B.attrs).forEach(function(H){var j=D(H,2),ee=j[0],M=j[1];B.attrs&&(B.attrs[ee]=o.replaceSensitiveString(M))})};try{for(var P=q(p),R=P.next();!R.done;R=P.next()){var O=R.value;x(O)}}catch(B){v={error:B}}finally{try{R&&!R.done&&(h=P.return)&&h.call(P)}finally{if(v)throw v.error}}var L=performance.now();return(m=o.diagnosticsClient)===null||m===void 0||m.recordHistogram("autocapturePlugin.getHierarchy",L-E),p},this.getNearestLabel=function(d){var v=d.parentElement;if(!v)return"";var h;try{h=v.querySelector(":scope>span,h1,h2,h3,h4,h5,h6")}catch{h=null}return h?o.getText(h):o.getNearestLabel(v)},this.getElementPath=function(d){var v;if(!d)return"";var h=performance.now(),g=gv(d),m=performance.now();return(v=o.diagnosticsClient)===null||v===void 0||v.recordHistogram("autocapturePlugin.getElementPath",m-h),g},this.getEventProperties=function(d,v,h){var g,m,E,p,y=(E=(m=v?.tagName)===null||m===void 0?void 0:m.toLowerCase)===null||E===void 0?void 0:E.call(m),b=typeof v.getBoundingClientRect=="function"?v.getBoundingClientRect():{left:null,top:null},S=o.getHierarchy(v),w=(p=S[0])===null||p===void 0?void 0:p.attrs,A=o.getNearestLabel(v),I=Vf(w??{},h),k=(g={},g[wf]=S,g[No]=y,g[Lo]=o.getText(v),g[_f]=b.left==null?null:Math.round(b.left),g[If]=b.top==null?null:Math.round(b.top),g[Cf]=I,g[kf]=o.getElementPath(v),g[Pf]=A,g[Wn]=dt(window.location.href.split("?")[0]),g[Of]=Si(o.replaceSensitiveString),g[Ds]=window.innerHeight,g[Ms]=window.innerWidth,g),x=js();if(x&&(k[Us]=x),k[Ef]=v.getAttribute("id")||"",k[Sf]=v.getAttribute("class"),k[Af]=w?.["aria-label"],y==="a"&&d==="click"&&v instanceof HTMLAnchorElement){var P=v.href.substring(0,qs);k[Tf]=o.replaceSensitiveString(P)}return Do(k)},this.addTypeAndTimestamp=function(d,v){return{event:d,timestamp:Date.now(),type:v}},this.addAdditionalEventProperties=function(d,v,h,g,m){m===void 0&&(m=!1);var E=o.addTypeAndTimestamp(d,v);if(Hf(E)&&E.event.target!==null){if(m){var p=Bs(E.event.target,E.type);if(p)return E.closestTrackedAncestor=E.event.target,E.targetElementProperties=o.getEventProperties(E.type,E.closestTrackedAncestor,g),E}var y=_i(E.event.target,h);return y&&(E.closestTrackedAncestor=y,E.targetElementProperties=o.getEventProperties(E.type,y,g)),E}return E},this.extractDataFromDataSource=function(d,v){if(d.sourceType==="DOM_ELEMENT"){var h=nv(d,v);return h?d.elementExtractType==="TEXT"?o.getText(h):d.elementExtractType==="ATTRIBUTE"&&d.attribute?h.getAttribute(d.attribute):void 0:void 0}},this.getTextWithMaskedDescendants=function(d){var v,h,g="[".concat(Pt,"], [contenteditable]");if(!d.querySelector(g))return d.innerText;var m="",E=Array.from(d.childNodes);try{for(var p=q(E),y=p.next();!y.done;y=p.next()){var b=y.value;if(b.nodeType===Node.TEXT_NODE){m+=b.textContent||"";continue}if(b instanceof Element){if(b.hasAttribute(Pt)||b.hasAttribute("contenteditable")){m+=ft;continue}m+=o.getTextWithMaskedDescendants(b)}}}catch(S){v={error:S}}finally{try{y&&!y.done&&(h=p.return)&&h.call(p)}finally{if(v)throw v.error}}return m},this.getText=function(d){var v=d.closest("[".concat(Pt,"]"))!==null;if(v)return ft;var h="";return d.querySelector("[".concat(Pt,"], [contenteditable]"))?h=o.getTextWithMaskedDescendants(d):h=d.innerText||"",o.replaceSensitiveString(h.substring(0,255)).replace(/\s+/g," ").trim()},this.getEventTagProps=function(d){var v,h,g;if(!d)return{};var m=(g=(h=d?.tagName)===null||h===void 0?void 0:h.toLowerCase)===null||g===void 0?void 0:g.call(h),E=(v={},v[No]=m,v[Lo]=o.getText(d),v[Wn]=window.location.href.split("?")[0],v);return Do(E)},this.diagnosticsClient=r?.diagnosticsClient;var s=(a=e.maskTextRegex)!==null&&a!==void 0?a:[],l=[];try{for(var u=q(s),c=u.next();!c.done;c=u.next()){var f=c.value;if(l.length>=Df)break;if(f instanceof RegExp)l.push(f);else if("pattern"in f&&typeof f.pattern=="string")try{l.push(new RegExp(f.pattern,"i"))}catch{}}}catch(d){n={error:d}}finally{try{c&&!c.done&&(i=u.return)&&i.call(u)}finally{if(n)throw n.error}}this.additionalMaskTextPatterns=l}return t})();function mv(t){var e=t.allObservables,r=t.onExposure,n=t.dataExtractor,i=t.exposureDuration,o=i===void 0?as:i,a=new Map,s=new Map,l=e.exposureObservable,u=l.subscribe(function(c){var f=c,d=f.target;if(f.isIntersecting){if(!a.get(d)){var v=setTimeout(function(){a.set(d,!0);var h=n.getElementPath(d);r(h),s.set(d,null)},o);s.set(d,v)}}else if(!f.isIntersecting&&f.intersectionRatio<1){var v=s.get(d);v&&(clearTimeout(v),s.set(d,null))}});return{unsubscribe:function(){u.unsubscribe()},reset:function(){s.forEach(function(c){c&&clearTimeout(c)}),s.clear(),a.clear()}}}function yv(t){var e,r,n,i,o=t.amplitude,a=t.scrollTracker,s=t.currentElementExposed,l=t.elementExposedForPage,u=t.exposureTracker,c=t.isPageEnd,f=t.lastScroll,d=a.getState(),v=F(),h=(r=v?.innerWidth)!==null&&r!==void 0?r:0,g=(n=v?.innerHeight)!==null&&n!==void 0?n:0,m=(e={},e[Wn]=(i=v?.location)===null||i===void 0?void 0:i.href,e[Rf]=d.maxX+h,e[xf]=d.maxY+g,e[Ds]=g,e[Ms]=h,e["[Amplitude] Element Exposed"]=Array.from(s),e),E=js();if(E&&(m[Us]=E),s.size===0&&d.maxX===f.maxX&&d.maxY===f.maxY){c&&(a.reset(),l.clear(),u?.reset());return}o?.track("[Amplitude] Viewport Content Updated",m),f.maxX=d.maxX,f.maxY=d.maxY,s.clear(),c&&(a.reset(),l.clear(),u?.reset())}function bv(t,e,r,n){if(!e.has(t)){e.add(t),r.add(t);var i=Array.from(r),o=JSON.stringify(i);o.length>=Uf&&n(!1)}}var Pe;(function(t){t.ClickObservable="clickObservable",t.ChangeObservable="changeObservable",t.NavigateObservable="navigateObservable",t.MutationObservable="mutationObservable",t.ScrollObservable="scrollObservable",t.ExposureObservable="exposureObservable",t.BrowserErrorObservable="browserErrorObservable",t.SelectionObservable="selectionObservable",t.MouseMoveObservable="mouseMoveObservable"})(Pe||(Pe={}));var Ev=function(t,e){var r,n,i,o,a,s,l,u,c,f,d,v;t===void 0&&(t={}),e?.diagnosticsClient.setTag("plugin.autocapture.version",Ns);var h=t.dataAttributePrefix,g=h===void 0?os:h,m=t.visualTaggingOptions,E=m===void 0?{enabled:!0}:m;t.cssSelectorAllowlist=(r=t.cssSelectorAllowlist)!==null&&r!==void 0?r:Uc,t.actionClickAllowlist=(n=t.actionClickAllowlist)!==null&&n!==void 0?n:Fc,t.debounceTime=(i=t.debounceTime)!==null&&i!==void 0?i:0;var p=((o=t.viewportContentUpdated)===null||o===void 0?void 0:o.enabled)!==!1,y=(l=(s=(a=t.viewportContentUpdated)===null||a===void 0?void 0:a.exposureDuration)!==null&&s!==void 0?s:t.exposureDuration)!==null&&l!==void 0?l:as;t.viewportContentUpdated=C(C({},t.viewportContentUpdated),{exposureDuration:y}),t.pageUrlExcludelist=(u=t.pageUrlExcludelist)===null||u===void 0?void 0:u.reduce(function(M,G){if(typeof G=="string"&&M.push(G),G instanceof RegExp&&M.push(G),typeof G=="object"&&G!==null&&"pattern"in G)try{M.push(new RegExp(G.pattern))}catch(W){return console.warn("Invalid regex pattern: ".concat(G.pattern),W),M}return M},[]);var b=vf,S="enrichment",w=[],A=new Ws(t,e),I=new Set,k=new Set,x,P=function(){var M,G=Ye(Gs().map(function(ie){return A.addAdditionalEventProperties(ie,"click",t.cssSelectorAllowlist,g)})),W=Ye(new we(function(ie){var oe,fe=function(ve){var me=A.addAdditionalEventProperties(ve,"change",t.cssSelectorAllowlist,g);ie.next(me)};return(oe=F())===null||oe===void 0||oe.document.addEventListener("change",fe,{capture:!0}),function(){var ve;return(ve=F())===null||ve===void 0?void 0:ve.document.removeEventListener("change",fe)}})),te;window.navigation&&(te=Ye(new we(function(ie){var oe=function(fe){var ve=A.addAdditionalEventProperties(fe,"navigate",t.cssSelectorAllowlist,g);ie.next(ve)};return window.navigation.addEventListener("navigate",oe),function(){window.navigation.removeEventListener("navigate",oe)}})));var J=Ye(Hs().map(function(ie){return A.addAdditionalEventProperties(ie,"mutation",t.cssSelectorAllowlist,g)})),Z=Xf(),K=Jf(J,t.cssSelectorAllowlist);return M={},M[Pe.ChangeObservable]=W,M[Pe.ClickObservable]=G,M[Pe.MutationObservable]=J,M[Pe.NavigateObservable]=te,M[Pe.ScrollObservable]=Z,M[Pe.ExposureObservable]=K,M},R=Uo(Object.values((f=(c=t.pageActions)===null||c===void 0?void 0:c.labeledEvents)!==null&&f!==void 0?f:{})),O=Fo((v=(d=t.pageActions)===null||d===void 0?void 0:d.triggers)!==null&&v!==void 0?v:[]),L=uv(R,O,A,t),B=function(M){var G,W;M&&(t.pageActions=C(C({},t.pageActions),M),R=Uo(Object.values((G=t.pageActions.labeledEvents)!==null&&G!==void 0?G:{})),O=Fo((W=t.pageActions.triggers)!==null&&W!==void 0?W:[]),L.update(R,O,t))},H=function(M,G){return _(void 0,void 0,void 0,function(){var W,te,J,Z,K,ie,oe,fe,ve,me,N,he,qe,Ae,Re,Be,Ve,We,Ke,ge,Ne;return T(this,function(nt){return typeof document>"u"?[2]:(W=!1,te={maxX:void 0,maxY:void 0},M.fetchRemoteConfig&&(M.remoteConfigClient?M.remoteConfigClient.subscribe("configs.analyticsSDK.pageActions","all",function(V){B(V)}):M.loggerProvider.debug("Remote config client is not provided, skipping remote config fetch")),J=Nt(t,t.cssSelectorAllowlist),Z=Nt(t,t.actionClickAllowlist),K=P(),ie=Wf({allObservables:K,amplitude:G,shouldTrackEvent:J,evaluateTriggers:L.evaluate.bind(L)}),w.push(ie),oe=Kf({allObservables:K,getEventProperties:function(){for(var V=[],z=0;zqo||t.xMax-t.xMin>qo}function Vo(t){if(t.length===0)return null;var e=t[0],r=t[t.length-1],n=C({"[Amplitude] Begin Time":new Date(e.timestamp).toISOString(),"[Amplitude] End Time":new Date(r.timestamp).toISOString(),"[Amplitude] Duration":r.timestamp-e.timestamp,"[Amplitude] Clicks":t.map(function(i){return{X:i.event.pageX,Y:i.event.pageY,Time:i.timestamp}}),"[Amplitude] Click Count":t.length},e.targetElementProperties);return{rageClickEvent:n,time:e.timestamp}}function _v(t,e){var r=Math.max(0,t.length-Ks+1),n=t[r];return e.timestamp-n.timestamp>=zs}function Iv(t,e){return t.length>0&&t[t.length-1].closestTrackedAncestor!==e.closestTrackedAncestor}function Av(t){var e=this,r=t.amplitude,n=t.allObservables,i=t.shouldTrackRageClick,o=n.clickObservable,a=n.selectionObservable,s=[],l={},u=null;function c(h){s=[],l={},h&&(Bo(l,h),s.push(h))}var f=wi(o.filter(function(h){return i("click",h.closestTrackedAncestor)}),function(h){return _(e,void 0,void 0,function(){var g;return T(this,function(m){return Bo(l,h),g=null,s.length===0||Iv(s,h)||_v(s,h)||l.isOutOfBounds?(u&&(g=Vo(s)),c(h)):s.push(h),u&&(clearTimeout(u.timerId),u.resolve(g),u=null),s.length>=Ks?[2,new Promise(function(E){u={resolve:E,timerId:setTimeout(function(){E(Vo(s))},zs)}})]:[2,null]})})}),d=a?.subscribe(function(){c()}),v=f.subscribe(function(h){h!==null&&r.track(pf,h.rageClickEvent,{time:h.time})});return{unsubscribe:function(){v.unsubscribe(),d?.unsubscribe()}}}var Cv=2e3;function kv(t){var e=t.amplitude,r=t.allObservables,n=t.shouldTrackErrorClick,i=r.clickObservable,o=r.browserErrorObservable,a=i.filter(function(c){return fr(c)&&n("click",c.closestTrackedAncestor)&&c.event.target instanceof Element&&c.event.target.closest('a[target="_blank"]')===null&&c.event.button===Yr.LEFT_OR_TOUCH_CONTACT}),s=null,l=null,u=function(){s!==null&&(clearTimeout(s),s=null),l=null};return Et(a,o).subscribe(function(c){var f;if(c.type==="click"){u(),l=c,s=setTimeout(u,Cv);return}c.type==="error"&&l&&(e.track(mf,C((f={},f["[Amplitude] Kind"]=c.event.kind,f["[Amplitude] Message"]=c.event.message,f["[Amplitude] Stack"]=c.event.stack,f["[Amplitude] Filename"]=c.event.filename,f["[Amplitude] Line Number"]=c.event.lineNumber,f["[Amplitude] Column Number"]=c.event.columnNumber,f),l.targetElementProperties)),u())})}var Xe;(function(t){t.INCREASING="increasing",t.DECREASING="decreasing"})(Xe||(Xe={}));var mt;(function(t){t.X="x",t.Y="y"})(mt||(mt={}));var Pv=function(t){var e=t.allWindowObservables,r=e.mouseMoveObservable;return new we(function(n){var i=null,o=null,a=null;return r.subscribe(function(s){var l={x:s.clientX,y:s.clientY};if(i===null){i=l;return}l.x>i.x?(o===Xe.DECREASING&&n.next(mt.X),o=Xe.INCREASING):l.xi.y?(a===Xe.DECREASING&&n.next(mt.Y),a=Xe.INCREASING):l.yn&&r.shift()}function jo(t){var e=t.changes,r=t.changesThreshold,n=t.thresholdMs;if(e.length"u"?[2]:(S=g(),s&&(w=Nt(t,u),A=Av({allObservables:S,amplitude:b,shouldTrackRageClick:w}),i.push(A)),a&&(I=Nt(t,c),k=Tv({amplitude:b,allObservables:S,getEventProperties:function(j,ee){return v.getEventProperties(j,ee,d)},shouldTrackDeadClick:I}),i.push(k)),o&&(x=Nt(t,f),P=kv({amplitude:b,allObservables:S,shouldTrackErrorClick:x}),i.push(P)),l&&(R=void 0,O=void 0,typeof t.thrashedCursor=="object"&&(R=t.thrashedCursor.directionChanges,O=t.thrashedCursor.threshold,R&&RSr&&(y.loggerProvider.warn("'thrashedCursor.threshold' of ".concat(O," is above the maximum of ").concat(Sr,", setting to ").concat(Sr)),O=Sr)),L=Nv({amplitude:b,options:t,allObservables:S,directionChanges:R,thresholdMs:O}),i.push(L)),(B=y?.loggerProvider)===null||B===void 0||B.log("".concat(r," has been successfully added.")),[2])})})},E=function(y){return _(void 0,void 0,void 0,function(){return T(this,function(b){return[2,y]})})},p=function(){return _(void 0,void 0,void 0,function(){var y,b,S,w,A;return T(this,function(I){try{for(y=q(i),b=y.next();!b.done;b=y.next())S=b.value,S.unsubscribe()}catch(k){w={error:k}}finally{try{b&&!b.done&&(A=y.return)&&A.call(y)}finally{if(w)throw w.error}}return[2]})})};return{name:r,type:n,setup:m,execute:E,teardown:p}},Dv="@amplitude/plugin-network-capture-browser",Ys="[Amplitude] Network Request",Js="500-599";function Lr(t,e){var r=e.replace(/[-[\]{}()+?.,\\^$|#\s]/g,"\\$&"),n="^"+r.replace(/\*/g,".*")+"$",i=new RegExp(n);return i.test(t)}function Qs(t,e){var r,n,i=e.split(",");try{for(var o=q(i),a=o.next();!a.done;a=o.next()){var s=a.value,l=D(s.split("-").map(Number),2),u=l[0],c=l[1];if(t===u&&c===void 0||t>=u&&t<=c)return!0}}catch(f){r={error:f}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return!1}function Mv(t,e,r,n,i){if(!(t.hosts&&!t.hosts.find(function(a){return Lr(e,a)}))&&!(n&&t.urls&&!Bn(n,t.urls))&&!(i&&t.methods&&!t.methods.find(function(a){return i.toLowerCase()===a.toLowerCase()||a==="*"}))){if(r||r===0){var o=t.statusCodeRange||Js;if(!Qs(r,o))return!1}return!0}}function Zs(t){var e;if(t)try{var r=(e=F())===null||e===void 0?void 0:e.location.href,n=new URL(t,r),i=n.searchParams.toString(),o=n.hash.replace("#",""),a=n.href,s=n.host;n.hash="",n.search="";var l=n.href;return{query:i,fragment:o,href:a,hrefWithoutQueryOrHash:l,host:s}}catch{return}}function Uv(t,e){if(t.includes("amplitude.com"))try{var r=e.body;if(typeof r!="string")return!1;var n=JSON.parse(r),i=n.events;if(i.find(function(o){return o.event_type===Ys}))return!0}catch{}return!1}function Wo(t){if(typeof t!="object"||t===null){if(t)return $([],D(Wr),!1);if(t===void 0){var e=void 0;return e}return}if(t.length!==0)return t}function Ko(t){var e,r;return!(!((e=t?.allowlist)===null||e===void 0)&&e.length)&&!(!((r=t?.blocklist)===null||r===void 0)&&r.length)}function Fv(t,e){var r;e===void 0&&(e={});var n=Zs(t.url);if(!n)return!1;var i=n.host;if(e.ignoreAmplitudeRequests!==!1&&(Lr(i,"*.amplitude.com")||Lr(i,"amplitude.com"))||!((r=e.ignoreHosts)===null||r===void 0)&&r.find(function(a){return Lr(i,a)})||!e.captureRules&&t.status!==void 0&&!Qs(t.status,Js))return!1;if(e.captureRules){var o;if($([],D(e.captureRules),!1).reverse().find(function(a){if(o=Mv(a,i,t.status,t.url,t.method),o){var s=Wo(a.responseHeaders);if(t.responseWrapper&&s){var l=t.responseWrapper.headers(s);l&&(t.responseHeaders=l)}var u=Wo(a.requestHeaders);if(t.requestWrapper&&u){var c=t.requestWrapper.headers(u);c&&(t.requestHeaders=c)}t.responseWrapper&&a.responseBody&&!Ko(a.responseBody)&&(t.responseBodyJson=t.responseWrapper.json(a.responseBody.allowlist,a.responseBody.blocklist)),t.requestWrapper&&a.requestBody&&!Ko(a.requestBody)&&(t.requestBodyJson=t.requestWrapper.json(a.requestBody.allowlist,a.requestBody.blocklist))}return o!==void 0}),!o)return!1}return!(t.requestWrapper&&Uv(i,t.requestWrapper))}function qv(t,e,r,n){return _(this,void 0,void 0,function(){var i,o,a;return T(this,function(s){switch(s.label){case 0:return e.requestBodyJson||e.responseBodyJson?[4,Promise.all([e.requestBodyJson,e.responseBodyJson])]:[3,2];case 1:if(i=D.apply(void 0,[s.sent(),2]),o=i[0],a=i[1],o)try{t["[Amplitude] Request Body"]=JSON.stringify(o)}catch(l){n?.debug("Failed to stringify request body",l)}if(a)try{t["[Amplitude] Response Body"]=JSON.stringify(a)}catch{n?.debug("Failed to stringify response body")}s.label=2;case 2:return r?.track(Ys,t),[2]}})})}function Bv(t){var e=t.allObservables,r=t.networkTrackingOptions,n=t.amplitude,i=t.loggerProvider,o=e.networkObservable,a=o.filter(function(s){return Fv(s.event,r)});return a.subscribe(function(s){var l,u,c,f=s.event,d=Zs(f.url);if(d){var v=(u=f.responseWrapper)===null||u===void 0?void 0:u.bodySize,h=(c=f.requestWrapper)===null||c===void 0?void 0:c.bodySize,g=(l={},l["[Amplitude] URL"]=d.hrefWithoutQueryOrHash,l["[Amplitude] URL Query"]=d.query,l["[Amplitude] URL Fragment"]=d.fragment,l["[Amplitude] Request Method"]=f.method,l["[Amplitude] Status Code"]=f.status,l["[Amplitude] Start Time"]=f.startTime,l["[Amplitude] Completion Time"]=f.endTime,l["[Amplitude] Duration"]=f.duration,l["[Amplitude] Request Body Size"]=h,l["[Amplitude] Response Body Size"]=v,l["[Amplitude] Request Type"]=f.type,l["[Amplitude] Request Headers"]=f.requestHeaders,l["[Amplitude] Response Headers"]=f.responseHeaders,l);qv(g,f,n,i)}})}var zn;(function(t){t.NetworkObservable="networkObservable"})(zn||(zn={}));var zo,Vv=function(t){t===void 0&&(t={});var e=Dv,r="enrichment",n,i=function(u,c){var f={event:u,timestamp:Date.now(),type:c};return f},o=function(){var u,c=new we(function(f){var d=new ed(function(v){var h=i(v,"network");f.next(h)});return po.subscribe(d,n),function(){po.unsubscribe(d)}});return u={},u[zn.NetworkObservable]=c,u},a=function(u,c){return _(void 0,void 0,void 0,function(){var f;return T(this,function(d){return typeof document>"u"?[2]:(f=o(),n=u?.loggerProvider,zo=Bv({allObservables:f,networkTrackingOptions:t,amplitude:c,loggerProvider:n}),n?.log("".concat(e," has been successfully added.")),[2])})})},s=function(u){return _(void 0,void 0,void 0,function(){return T(this,function(c){return[2,u]})})},l=function(){return _(void 0,void 0,void 0,function(){return T(this,function(u){return zo.unsubscribe(),[2]})})};return{name:e,type:r,setup:a,execute:s,teardown:l}},jv="web-vitals-browser",Hv="[Amplitude] Web Vitals";let eu=-1;const qt=t=>{addEventListener("pageshow",(e=>{e.persisted&&(eu=e.timeStamp,t(e))}),!0)},Qe=(t,e,r,n)=>{let i,o;return a=>{e.value>=0&&(a||n)&&(o=e.value-(i??0),(o||i===void 0)&&(i=e.value,e.delta=o,e.rating=((s,l)=>s>l[1]?"poor":s>l[0]?"needs-improvement":"good")(e.value,r),t(e)))}},Ii=t=>{requestAnimationFrame((()=>requestAnimationFrame((()=>t()))))},Ai=()=>{const t=performance.getEntriesByType("navigation")[0];if(t&&t.responseStart>0&&t.responseStartAi()?.activationStart??0,Ze=(t,e=-1)=>{const r=Ai();let n="navigate";return eu>=0?n="back-forward-cache":r&&(document.prerendering||vr()>0?n="prerender":document.wasDiscarded?n="restore":r.type&&(n=r.type.replace(/_/g,"-"))),{name:t,value:e,rating:"good",delta:0,entries:[],id:`v5-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:n}},_n=new WeakMap;function Ci(t,e){return _n.get(t)||_n.set(t,new e),_n.get(t)}class Gv{t;i=0;o=[];h(e){if(e.hadRecentInput)return;const r=this.o[0],n=this.o.at(-1);this.i&&r&&n&&e.startTime-n.startTime<1e3&&e.startTime-r.startTime<5e3?(this.i+=e.value,this.o.push(e)):(this.i=e.value,this.o=[e]),this.t?.(e)}}const hr=(t,e,r={})=>{try{if(PerformanceObserver.supportedEntryTypes.includes(t)){const n=new PerformanceObserver((i=>{Promise.resolve().then((()=>{e(i.getEntries())}))}));return n.observe({type:t,buffered:!0,...r}),n}}catch{}},ki=t=>{let e=!1;return()=>{e||(t(),e=!0)}};let Ot=-1;const tu=new Set,$o=()=>document.visibilityState!=="hidden"||document.prerendering?1/0:0,$n=t=>{if(document.visibilityState==="hidden"){if(t.type==="visibilitychange")for(const e of tu)e();isFinite(Ot)||(Ot=t.type==="visibilitychange"?t.timeStamp:0,removeEventListener("prerenderingchange",$n,!0))}},rn=()=>{if(Ot<0){const t=vr();Ot=(document.prerendering?void 0:globalThis.performance.getEntriesByType("visibility-state").filter((r=>r.name==="hidden"&&r.startTime>t))[0]?.startTime)??$o(),addEventListener("visibilitychange",$n,!0),addEventListener("prerenderingchange",$n,!0),qt((()=>{setTimeout((()=>{Ot=$o()}))}))}return{get firstHiddenTime(){return Ot},onHidden(t){tu.add(t)}}},nn=t=>{document.prerendering?addEventListener("prerenderingchange",(()=>t()),!0):t()},Xo=[1800,3e3],ru=(t,e={})=>{nn((()=>{const r=rn();let n,i=Ze("FCP");const o=hr("paint",(a=>{for(const s of a)s.name==="first-contentful-paint"&&(o.disconnect(),s.startTime{i=Ze("FCP"),n=Qe(t,i,Xo,e.reportAllChanges),Ii((()=>{i.value=performance.now()-a.timeStamp,n(!0)}))})))}))},Yo=[.1,.25],Wv=(t,e={})=>{const r=rn();ru(ki((()=>{let n,i=Ze("CLS",0);const o=Ci(e,Gv),a=l=>{for(const u of l)o.h(u);o.i>i.value&&(i.value=o.i,i.entries=o.o,n())},s=hr("layout-shift",a);s&&(n=Qe(t,i,Yo,e.reportAllChanges),r.onHidden((()=>{a(s.takeRecords()),n(!0)})),qt((()=>{o.i=0,i=Ze("CLS",0),n=Qe(t,i,Yo,e.reportAllChanges),Ii((()=>n()))})),setTimeout(n))})))};let nu=0,In=1/0,wr=0;const Kv=t=>{for(const e of t)e.interactionId&&(In=Math.min(In,e.interactionId),wr=Math.max(wr,e.interactionId),nu=wr?(wr-In)/7+1:0)};let Xn;const Jo=()=>Xn?nu:performance.interactionCount??0,zv=()=>{"interactionCount"in performance||Xn||(Xn=hr("event",Kv,{type:"event",buffered:!0,durationThreshold:0}))};let Qo=0,$v=class{u=[];l=new Map;m;p;v(){Qo=Jo(),this.u.length=0,this.l.clear()}L(){const e=Math.min(this.u.length-1,Math.floor((Jo()-Qo)/50));return this.u[e]}h(e){if(this.m?.(e),!e.interactionId&&e.entryType!=="first-input")return;const r=this.u.at(-1);let n=this.l.get(e.interactionId);if(n||this.u.length<10||e.duration>r.P){if(n?e.duration>n.P?(n.entries=[e],n.P=e.duration):e.duration===n.P&&e.startTime===n.entries[0].startTime&&n.entries.push(e):(n={id:e.interactionId,entries:[e],P:e.duration},this.l.set(n.id,n),this.u.push(n)),this.u.sort(((i,o)=>o.P-i.P)),this.u.length>10){const i=this.u.splice(10);for(const o of i)this.l.delete(o.id)}this.p?.(n)}}};const iu=t=>{const e=globalThis.requestIdleCallback||setTimeout;document.visibilityState==="hidden"?t():(t=ki(t),addEventListener("visibilitychange",t,{once:!0,capture:!0}),e((()=>{t(),removeEventListener("visibilitychange",t,{capture:!0})})))},Zo=[200,500],Xv=(t,e={})=>{if(!globalThis.PerformanceEventTiming||!("interactionId"in PerformanceEventTiming.prototype))return;const r=rn();nn((()=>{zv();let n,i=Ze("INP");const o=Ci(e,$v),a=l=>{iu((()=>{for(const c of l)o.h(c);const u=o.L();u&&u.P!==i.value&&(i.value=u.P,i.entries=u.entries,n())}))},s=hr("event",a,{durationThreshold:e.durationThreshold??40});n=Qe(t,i,Zo,e.reportAllChanges),s&&(s.observe({type:"first-input",buffered:!0}),r.onHidden((()=>{a(s.takeRecords()),n(!0)})),qt((()=>{o.v(),i=Ze("INP"),n=Qe(t,i,Zo,e.reportAllChanges)})))}))};let Yv=class{m;h(e){this.m?.(e)}};const ea=[2500,4e3],Jv=(t,e={})=>{nn((()=>{const r=rn();let n,i=Ze("LCP");const o=Ci(e,Yv),a=l=>{e.reportAllChanges||(l=l.slice(-1));for(const u of l)o.h(u),u.startTime{a(s.takeRecords()),s.disconnect(),n(!0)})),u=c=>{c.isTrusted&&(iu(l),removeEventListener(c.type,u,{capture:!0}))};for(const c of["keydown","click","visibilitychange"])addEventListener(c,u,{capture:!0});qt((c=>{i=Ze("LCP"),n=Qe(t,i,ea,e.reportAllChanges),Ii((()=>{i.value=performance.now()-c.timeStamp,n(!0)}))}))}}))},ta=[800,1800],Yn=t=>{document.prerendering?nn((()=>Yn(t))):document.readyState!=="complete"?addEventListener("load",(()=>Yn(t)),!0):setTimeout(t)},Qv=(t,e={})=>{let r=Ze("TTFB"),n=Qe(t,r,ta,e.reportAllChanges);Yn((()=>{const i=Ai();i&&(r.value=Math.max(i.responseStart-vr(),0),r.entries=[i],n(!0),qt((()=>{r=Ze("TTFB",0),n=Qe(t,r,ta,e.reportAllChanges),n(!0)})))}))};function Zv(t){var e,r=((e=t.entries[0])===null||e===void 0?void 0:e.startTime)||0;return performance.timeOrigin+r}function Kt(t){return{value:t.value,rating:t.rating,delta:t.delta,navigationType:t.navigationType,id:t.id,timestamp:Math.floor(Zv(t)),navigationStart:Math.floor(performance.timeOrigin)}}var eh=function(){var t=null,e=F(),r=e?.document,n=e?.location,i=function(s,l){return _(void 0,void 0,void 0,function(){var u,c;return T(this,function(f){return r===void 0?[2]:(u=dt(n?.href||"",s.loggerProvider),c={"[Amplitude] Page Domain":n?.hostname||"","[Amplitude] Page Location":u,"[Amplitude] Page Path":dt(n?.pathname||"",s.loggerProvider),"[Amplitude] Page Title":typeof document<"u"&&document.title||"","[Amplitude] Page URL":dt(u.split("?")[0],s.loggerProvider)},Jv(function(d){c["[Amplitude] LCP"]=Kt(d)}),ru(function(d){c["[Amplitude] FCP"]=Kt(d)}),Xv(function(d){c["[Amplitude] INP"]=Kt(d)}),Wv(function(d){c["[Amplitude] CLS"]=Kt(d)}),Qv(function(d){c["[Amplitude] TTFB"]=Kt(d)}),t=function(){r.visibilityState==="hidden"&&t&&(l.track(Hv,c),r.removeEventListener("visibilitychange",t),t=null)},r.addEventListener("visibilitychange",t),[2])})})},o=function(s){return _(void 0,void 0,void 0,function(){return T(this,function(l){return[2,s]})})},a=function(){return _(void 0,void 0,void 0,function(){return T(this,function(s){return t&&r?.removeEventListener("visibilitychange",t),[2]})})};return{name:jv,type:"enrichment",setup:i,execute:o,teardown:a}},ra=(function(){function t(e,r){var n;this.shouldTrackNewCampaign=!1,this.options=C({initialEmptyValue:"EMPTY",resetSessionOnNewCampaign:!1,excludeReferrers:$d(((n=r.cookieOptions)===null||n===void 0?void 0:n.domain)||r.topLevelDomain),optOut:r.optOut},e),this.storage=r.cookieStorage,this.storageKey=so(r.apiKey,"MKTG"),this.webExpStorageKey=so(r.apiKey,"MKTG_ORIGINAL"),this.currentCampaign=Zr,this.sessionTimeout=r.sessionTimeout,this.lastEventTime=r.lastEventTime,this.logger=r.loggerProvider,this.topLevelDomain=r.topLevelDomain,r.loggerProvider.log("Installing web attribution tracking.")}return t.prototype.init=function(){return _(this,void 0,void 0,function(){var e,r;return T(this,function(n){switch(n.label){case 0:return this.options.optOut?[2]:[4,this.fetchCampaign()];case 1:return r=D.apply(void 0,[n.sent(),2]),this.currentCampaign=r[0],this.previousCampaign=r[1],e=this.lastEventTime?Ja(this.sessionTimeout,this.lastEventTime):!0,Gd(this.currentCampaign,this.previousCampaign,this.options,this.logger,e,this.topLevelDomain)?(this.shouldTrackNewCampaign=!0,[4,this.storage.set(this.storageKey,this.currentCampaign)]):[3,3];case 2:n.sent(),n.label=3;case 3:return[2]}})})},t.prototype.fetchCampaign=function(){return _(this,void 0,void 0,function(){var e;return T(this,function(r){switch(r.label){case 0:return[4,this.storage.get(this.webExpStorageKey)];case 1:return e=r.sent(),e?[4,this.storage.remove(this.webExpStorageKey)]:[3,3];case 2:r.sent(),r.label=3;case 3:return[4,Promise.all([e||new fs().parse(),this.storage.get(this.storageKey)])];case 4:return[2,r.sent()]}})})},t.prototype.generateCampaignEvent=function(e){this.shouldTrackNewCampaign=!1;var r=zd(this.currentCampaign,this.options);return e&&(r.event_id=e),r},t.prototype.shouldSetSessionIdOnNewCampaign=function(){return this.shouldTrackNewCampaign&&!!this.options.resetSessionOnNewCampaign},t})(),Tr="AMP_CURRENT_PAGE",_r="AMP_PREVIOUS_PAGE",At="AMP_URL_INFO",ir;(function(t){t.Direct="direct",t.Internal="internal",t.External="external"})(ir||(ir={}));var th=new Set([xe.IDENTIFY,xe.GROUP_IDENTIFY,xe.REVENUE]),rh=function(t){var e={},r=e.internalDomains,n=r===void 0?[]:r,i=F(),o=void 0,a=!1,s=void 0,l=!1,u=!1,c=function(g){var m;try{var E=dt(g,s);m=new URL(E).hostname}catch(p){s?.error("Could not parse URL: ",p)}return m},f=function(g){var m=typeof location<"u"&&location.hostname||"",E=g?c(g):void 0;if(!E)return ir.Direct;var p=n.some(function(b){return m.indexOf(b)!==-1}),y=n.some(function(b){return E.indexOf(b)!==-1});return m===E||y&&p?ir.Internal:ir.External},d=function(){return _(void 0,void 0,void 0,function(){var g,m,E,p,y;return T(this,function(b){switch(b.label){case 0:return o&&a?[4,o.get(At)]:[3,3];case 1:return g=b.sent(),m=dt(typeof location<"u"&&location.href||""),E=g?.[Tr]||"",p=void 0,m===E?p=g?.[_r]||"":E?p=E:p=document.referrer||"",[4,o.set(At,(y={},y[Tr]=m,y[_r]=p,y))];case 2:b.sent(),b.label=3;case 3:return[2]}})})},v=function(){d()},h={name:"@amplitude/plugin-page-url-enrichment-browser",type:"enrichment",setup:function(g,m){return _(void 0,void 0,void 0,function(){var E;return T(this,function(p){switch(p.label){case 0:if(s=g.loggerProvider,s.log("Installing @amplitude/plugin-page-url-enrichment-browser"),u=!0,!i)return[3,2];try{o=new en(i.sessionStorage)}catch{s?.debug("sessionStorage is not available in this environment.")}return[4,o?.isEnabled()];case 1:a=(E=p.sent())!==null&&E!==void 0?E:!1,i.addEventListener("popstate",v),l||(i.history.pushState=new Proxy(i.history.pushState,{apply:function(y,b,S){var w=D(S,3),A=w[0],I=w[1],k=w[2];y.apply(b,[A,I,k]),u&&v()}}),i.history.replaceState=new Proxy(i.history.replaceState,{apply:function(y,b,S){var w=D(S,3),A=w[0],I=w[1],k=w[2];y.apply(b,[A,I,k]),u&&v()}}),l=!0),p.label=2;case 2:return[2]}})})},execute:function(g){return _(void 0,void 0,void 0,function(){var m,E,p,y,b;return T(this,function(S){switch(S.label){case 0:return m=dt(typeof location<"u"&&location.href||""),o&&a?[4,o.get(At)]:[3,5];case 1:return E=S.sent(),E?.[Tr]?[3,3]:[4,o.set(At,(b={},b[Tr]=m,b[_r]=document.referrer||"",b))];case 2:S.sent(),S.label=3;case 3:return[4,o.get(At)];case 4:if(p=S.sent(),y="",p&&(y=p[_r]||""),th.has(g.event_type))return[2,g];g.event_properties=C(C({},g.event_properties||{}),{"[Amplitude] Page Domain":zt(g,"[Amplitude] Page Domain",typeof location<"u"&&location.hostname||""),"[Amplitude] Page Location":zt(g,"[Amplitude] Page Location",m),"[Amplitude] Page Path":zt(g,"[Amplitude] Page Path",typeof location<"u"&&dt(location.pathname)||""),"[Amplitude] Page Title":zt(g,"[Amplitude] Page Title",Si(Ei)),"[Amplitude] Page URL":zt(g,"[Amplitude] Page URL",m.split("?")[0]),"[Amplitude] Previous Page Location":y,"[Amplitude] Previous Page Type":f(y)}),S.label=5;case 5:return[2,g]}})})},teardown:function(){return _(void 0,void 0,void 0,function(){return T(this,function(g){switch(g.label){case 0:return i&&(i.removeEventListener("popstate",v),u=!1),o&&a?[4,o.set(At,{})]:[3,2];case 1:g.sent(),g.label=2;case 2:return[2]}})})}};return h};function zt(t,e,r){return t.event_properties||(t.event_properties={}),t.event_properties[e]===void 0?r:t.event_properties[e]}var nh=function(){var t,e,r;function n(a){return typeof a!="object"||a===null?!1:"body"in a&&typeof a.body=="string"}function i(a){if(a)try{var s=new Function("return "+a)();if(typeof s=="function")return s;t?.error("Custom enrichment body did not evaluate to a function")}catch(l){t?.error("Could not create custom enrichment function",l)}return function(l){return l}}var o={name:"@amplitude/plugin-custom-enrichment-browser",type:"enrichment",setup:function(a,s){return _(void 0,void 0,void 0,function(){var l,u;return T(this,function(c){return t=a.loggerProvider,t?.log("Installing @amplitude/plugin-custom-enrichment-browser"),!((u=a.remoteConfig)===null||u===void 0)&&u.fetchRemoteConfig&&(a.remoteConfigClient?(l=a.remoteConfigClient.subscribe("configs.analyticsSDK.browserSDK.customEnrichment","all",function(f){f&&n(f)?r=i(f.body||""):r=i("")}),e=function(){var f;return(f=a.remoteConfigClient)===null||f===void 0?void 0:f.unsubscribe(l)}):t?.debug("Remote config client is not provided, skipping remote config fetch")),[2]})})},execute:function(a){return _(void 0,void 0,void 0,function(){var s;return T(this,function(l){if(r)try{return[2,(s=r(a))!==null&&s!==void 0?s:null]}catch(u){return t?.error("Could not execute custom enrichment function",u),[2,a]}return[2,a]})})},teardown:function(){return _(void 0,void 0,void 0,function(){return T(this,function(a){return e&&e(),[2]})})}};return o},An=-1,ih=(function(t){Fe(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r._diagnosticsSampleRate=0,r._enableRequestBodyCompressionExperimentalValue=!1,r}return e.prototype.init=function(r,n,i){r===void 0&&(r="");var o,a;return arguments.length>2?(o=n,a=i):typeof n=="string"?(o=n,a=void 0):(o=n?.userId,a=n),Te(this._init(C(C({},a),{userId:o,apiKey:r})))},e.prototype._init=function(r){var n,i,o,a,s,l,u,c;return _(this,void 0,void 0,function(){var f,d,v,h,g,m,E,p,y,b,S,w,A,I,k,x=this;return T(this,function(P){switch(P.label){case 0:return this.initializing?[2]:(this.initializing=!0,f=ef(r),d=(n=r.loggerProvider)!==null&&n!==void 0?n:new Dt,r.loggerProvider||d.enable((i=r.logLevel)!==null&&i!==void 0?i:ke.Warn),v=(o=r.serverZone)!==null&&o!==void 0?o:ks,g=this._diagnosticsSampleRate,m=(a=r.enableDiagnostics)!==null&&a!==void 0?a:!0,f?(h=new is(r.apiKey,d,v,(s=r.remoteConfig)===null||s===void 0?void 0:s.serverUrl),[4,new Promise(function(R){h?.subscribe("configs.diagnostics.browserSDK","all",function(O,L,B){if(d.debug("Diagnostics remote configuration received:",JSON.stringify({remoteConfig:O,source:L,lastFetch:B},null,2)),O){var H=O.sampleRate;typeof H=="number"&&!isNaN(H)&&(g=H);var j=O.enabled;typeof j=="boolean"&&(m=j)}R()})})]):[3,2]);case 1:P.sent(),P.label=2;case 2:return E=new _c(r.apiKey,d,v,{enabled:m,sampleRate:g}),E.setTag("library","".concat(As,"/").concat(Ti)),typeof navigator<"u"&&E.setTag("user_agent",navigator.userAgent),[4,Qd(r.apiKey,r,this,E,{loggerProvider:d,serverZone:v,enableDiagnostics:m,diagnosticsSampleRate:g})];case 3:return p=P.sent(),f&&h?[4,new Promise(function(R){h?.subscribe("configs.analyticsSDK.browserSDK","all",function(O,L,B){p.loggerProvider.debug("Remote configuration received:",JSON.stringify({remoteConfig:O,source:L,lastFetch:B},null,2)),O&&ff(O,p),R()})})]:[3,5];case 4:P.sent(),P.label=5;case 5:return[4,t.prototype._init.call(this,p)];case 6:return P.sent(),this.logBrowserOptions(p),this.config.remoteConfigClient=h,Es(this.config.defaultTracking)?(this.config.optOut&&this.timeline.addOptOutListener(function(R){return _(x,void 0,void 0,function(){var O;return T(this,function(L){switch(L.label){case 0:return R?[3,2]:(O=To(this.config),this.webAttribution=new ra(O,this.config),[4,this.webAttribution.init()]);case 1:L.sent(),L.label=2;case 2:return[2]}})})}),y=To(this.config),this.webAttribution=new ra(y,this.config),[4,this.webAttribution.init()]):[3,8];case 7:P.sent(),P.label=8;case 8:return b=Kr(),S=b.ampTimestamp?Number(b.ampTimestamp):void 0,w=S?Date.now()1||r<0)&&!this.config){this._diagnosticsSampleRate=r;return}},e.prototype._enableRequestBodyCompressionExperimental=function(r){if(!this.config){this._enableRequestBodyCompressionExperimentalValue=r;return}},e})(Vl),ou="[Amplitude]",oh="".concat(ou," Session Replay ID"),ah=0,sh=lr.US,uh={enabled:!0},Pi=1e3,lh="".concat(ou," Session Replay Debug"),ch="amp-block",au="amp-mask",dh="amp-unmask",su="https://api-sr.amplitude.com/sessions/v2/track",uu="https://api-sr.eu.amplitude.com/sessions/v2/track",lu="https://api-sr.stag2.amplitude.com/sessions/v2/track",fh=1*1e6,vh=3e4,hh=6e4,gh=500,ph=10*1e3,cu=1024,mh=1e3,at;(function(t){t.GET_SR_PROPS="get-sr-props",t.DEBUG_INFO="debug-info",t.FETCH_REQUEST="fetch-request",t.METADATA="metadata",t.TARGETING_DECISION="targeting-decision"})(at||(at={}));var Jn=(function(){function t(e){this.logger=e,this.log=this.getSafeMethod("log"),this.warn=this.getSafeMethod("warn"),this.error=this.getSafeMethod("error"),this.debug=this.getSafeMethod("debug")}return t.prototype.getSafeMethod=function(e){var r;if(!this.logger)return(function(){});var n=this.logger[e];if(typeof n=="function"){var i=(r=n.__rrweb_original__)!==null&&r!==void 0?r:n;return i.bind(this.logger)}return(function(){})},t.prototype.enable=function(e){this.logger.enable(e)},t.prototype.disable=function(){this.logger.disable()},t})(),Qn="medium";function yh(t){return t.toLowerCase()}function bh(t){var e=t.type;return t.hasAttribute("data-rr-is-password")?"password":e?yh(e):null}var du=function(t,e,r){switch(e){case"light":{if(t!=="input")return!0;var n=r?bh(r):"";return n?!!(["password","hidden","email","tel"].includes(n)||r.autocomplete.startsWith("cc-")):!1}case"medium":case"conservative":return!0;default:return du(t,Qn,r)}},fu=function(t,e,r){var n,i,o;if(e===void 0&&(e={defaultMaskLevel:Qn}),r){if(r.closest("."+au))return!0;var a=((n=e.maskSelector)!==null&&n!==void 0?n:[]).some(function(l){return r.closest(l)});if(a)return!0;if(r.closest("."+dh))return!1;var s=((i=e.unmaskSelector)!==null&&i!==void 0?i:[]).some(function(l){return r.closest(l)});if(s)return!1}return du(t,(o=e.defaultMaskLevel)!==null&&o!==void 0?o:Qn,r)},na=function(t,e){return function(r,n){return fu(t,e,n)?r.replace(/[^\s]/g,"*"):r}},Eh=function(t){return function(e,r,n){var i;return e==="style"||!((i=t?.maskAttributes)!==null&&i!==void 0?i:[]).includes(e)?r:fu("text",t,n)?r.replace(/[^\s]/g,"*"):r}},Sh=function(){var t=F();return t?.location?t.location.href:""},wh=function(t,e){return"".concat(e,"/").concat(t)},Oi=function(t,e){return e||(t===lr.STAGING?lu:t===lr.EU?uu:su)},Th=function(t){if(typeof t!="string"||t.trim()==="")return!1;var e=/^\/|^https?:\/\/[^\s]+$/;return!!e.test(t)},_h=function(t){var e=t.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,".");return new RegExp("^".concat(e,"$"))},Ih=function(t){if(!t.every(function(e){return typeof e.selector=="string"&&typeof e.replacement=="string"}))throw new Error("ugcFilterRules must be an array of objects with selector and replacement properties");if(!t.every(function(e){return Th(e.selector)}))throw new Error("ugcFilterRules must be an array of objects with valid globs")},on=function(t,e){var r,n;try{for(var i=q(e),o=i.next();!o.done;o=i.next()){var a=o.value,s=_h(a.selector);if(s.test(t))return t.replace(s,a.replacement)}}catch(l){r={error:l}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return t},vu=function(){return _(void 0,void 0,void 0,function(){var t,e,r,n,i,o,a;return T(this,function(s){switch(s.label){case 0:return s.trys.push([0,3,,4]),t=F(),t?[4,t.navigator.storage.estimate()]:[3,2];case 1:return e=s.sent(),r=e.usage,n=e.quota,i=e.usageDetails,o=r?Math.round(r/cu):0,a=r&&n?Math.round((r/n+Number.EPSILON)*1e3)/1e3:0,[2,{totalStorageSize:o,percentOfQuota:a,usageDetails:JSON.stringify(i)}];case 2:return[3,4];case 3:return s.sent(),[3,4];case 4:return[2,{totalStorageSize:0,percentOfQuota:0,usageDetails:""}]}})})},hu=function(t){var e=C({},t),r=e.apiKey;return e.apiKey="****".concat(r.substring(r.length-4)),e},gu=function(){return{flushMaxRetries:2,logLevel:ke.Warn,loggerProvider:new Dt,transportProvider:new Ic}},Ah=(function(t){Fe(e,t);function e(r,n){var i=this,o,a,s,l,u,c,f=gu();if(i=t.call(this,C(C({transportProvider:f.transportProvider,loggerProvider:new Jn(n.loggerProvider||f.loggerProvider)},n),{apiKey:r}))||this,i.flushMaxRetries=n.flushMaxRetries!==void 0&&n.flushMaxRetries<=f.flushMaxRetries?n.flushMaxRetries:f.flushMaxRetries,i.apiKey=r,i.sampleRate=n.sampleRate||ah,i.serverZone=n.serverZone||sh,i.configServerUrl=n.configServerUrl,i.trackServerUrl=n.trackServerUrl,i.shouldInlineStylesheet=n.shouldInlineStylesheet,i.version=n.version,i.performanceConfig=n.performanceConfig||uh,i.storeType=(o=n.storeType)!==null&&o!==void 0?o:"idb",i.applyBackgroundColorToBlockedElements=(a=n.applyBackgroundColorToBlockedElements)!==null&&a!==void 0?a:!1,i.enableUrlChangePolling=(s=n.enableUrlChangePolling)!==null&&s!==void 0?s:!1,i.urlChangePollingInterval=(l=n.urlChangePollingInterval)!==null&&l!==void 0?l:Pi,i.captureDocumentTitle=(u=n.captureDocumentTitle)!==null&&u!==void 0?u:!1,n.privacyConfig&&(i.privacyConfig=n.privacyConfig),n.interactionConfig&&(i.interactionConfig=n.interactionConfig,i.interactionConfig.ugcFilterRules&&Ih(i.interactionConfig.ugcFilterRules)),n.debugMode&&(i.debugMode=n.debugMode),n.useWebWorker!==void 0)i.useWebWorker=n.useWebWorker;else{var d=n;((c=d.experimental)===null||c===void 0?void 0:c.useWebWorker)!==void 0&&(i.useWebWorker=d.experimental.useWebWorker)}return n.omitElementTags&&(i.omitElementTags=n.omitElementTags),i}return e})(Xa),pu=(t=>(t[t.DomContentLoaded=0]="DomContentLoaded",t[t.Load=1]="Load",t[t.FullSnapshot=2]="FullSnapshot",t[t.IncrementalSnapshot=3]="IncrementalSnapshot",t[t.Meta=4]="Meta",t[t.Custom=5]="Custom",t[t.Plugin=6]="Plugin",t))(pu||{}),mu=(t=>(t[t.MouseUp=0]="MouseUp",t[t.MouseDown=1]="MouseDown",t[t.Click=2]="Click",t[t.ContextMenu=3]="ContextMenu",t[t.DblClick=4]="DblClick",t[t.Focus=5]="Focus",t[t.Blur=6]="Blur",t[t.TouchStart=7]="TouchStart",t[t.TouchMove_Departed=8]="TouchMove_Departed",t[t.TouchEnd=9]="TouchEnd",t[t.TouchCancel=10]="TouchCancel",t))(mu||{}),Ch=function(t,e){var r=document.createDocumentFragment(),n=function(i){if(i===void 0&&(i=[]),typeof i=="string"&&(i=[i]),i=i.filter(function(o){try{r.querySelector(o)}catch{return e.warn('[session-replay-browser] omitting selector "'.concat(o,'" because it is invalid')),!1}return!0}),i.length!==0)return i};return t.blockSelector=n(t.blockSelector),t.maskSelector=n(t.maskSelector),t.unmaskSelector=n(t.unmaskSelector),t},kh=(function(){function t(e,r){this.localConfig=r,this.remoteConfigClient=e}return t.prototype.generateJoinedConfig=function(){var e,r,n,i,o;return _(this,void 0,void 0,function(){var a,s,l,u,c,f,d,v,h,g,m,E,p,y,b,S,w,A=this;return T(this,function(I){switch(I.label){case 0:a=C({},this.localConfig),a.optOut=this.localConfig.optOut,a.captureEnabled=!0,I.label=1;case 1:return I.trys.push([1,3,,4]),[4,new Promise(function(k,x){A.remoteConfigClient.subscribe("configs.sessionReplay","all",function(P,R){var O;if(A.localConfig.loggerProvider.debug("Session Replay remote configuration received from ".concat(R,":"),JSON.stringify(P,null,2)),!P){x(new Error("No remote config received"));return}var L=P,B=L.sr_sampling_config,H=L.sr_privacy_config,j=L.sr_targeting_config,ee=(O=a.interactionConfig)===null||O===void 0?void 0:O.ugcFilterRules;a.interactionConfig=L.sr_interaction_config,a.interactionConfig&&ee&&(a.interactionConfig.ugcFilterRules=ee),a.loggingConfig=L.sr_logging_config,(B||H||j)&&(s={},B&&(s.sr_sampling_config=B),H&&(s.sr_privacy_config=H),j&&(s.sr_targeting_config=j)),k()})})];case 2:return I.sent(),[3,4];case 3:return l=I.sent(),this.localConfig.loggerProvider.error("Failed to generate joined config: ",l),a.captureEnabled=!1,[2,{localConfig:this.localConfig,joinedConfig:a,remoteConfig:void 0}];case 4:if(!s)return[2,{localConfig:this.localConfig,joinedConfig:a,remoteConfig:s}];if(u=s.sr_sampling_config,c=s.sr_privacy_config,f=s.sr_targeting_config,u&&Object.keys(u).length>0?(Object.prototype.hasOwnProperty.call(u,"capture_enabled")?a.captureEnabled=u.capture_enabled:a.captureEnabled=!1,Object.prototype.hasOwnProperty.call(u,"sample_rate")&&(a.sampleRate=u.sample_rate)):(a.captureEnabled=!0,this.localConfig.loggerProvider.debug("Remote config successfully fetched, but no values set for project, Session Replay capture enabled.")),c){d=(e=a.privacyConfig)!==null&&e!==void 0?e:{},v={defaultMaskLevel:(n=(r=c.defaultMaskLevel)!==null&&r!==void 0?r:d.defaultMaskLevel)!==null&&n!==void 0?n:"medium",blockSelector:[],maskSelector:[],unmaskSelector:[],maskAttributes:$([],D(new Set($($([],D((i=d.maskAttributes)!==null&&i!==void 0?i:[]),!1),D((o=c.maskAttributes)!==null&&o!==void 0?o:[]),!1))),!1)},h=function(k){var x,P,R,O,L,B,H,j,ee,M={};typeof k.blockSelector=="string"&&(k.blockSelector=[k.blockSelector]);try{for(var G=q((H=k.blockSelector)!==null&&H!==void 0?H:[]),W=G.next();!W.done;W=G.next()){var te=W.value;M[te]="block"}}catch(oe){x={error:oe}}finally{try{W&&!W.done&&(P=G.return)&&P.call(G)}finally{if(x)throw x.error}}try{for(var J=q((j=k.maskSelector)!==null&&j!==void 0?j:[]),Z=J.next();!Z.done;Z=J.next()){var te=Z.value;M[te]="mask"}}catch(oe){R={error:oe}}finally{try{Z&&!Z.done&&(O=J.return)&&O.call(J)}finally{if(R)throw R.error}}try{for(var K=q((ee=k.unmaskSelector)!==null&&ee!==void 0?ee:[]),ie=K.next();!ie.done;ie=K.next()){var te=ie.value;M[te]="unmask"}}catch(oe){L={error:oe}}finally{try{ie&&!ie.done&&(B=K.return)&&B.call(K)}finally{if(L)throw L.error}}return M},g=C(C({},h(d)),h(c));try{for(m=q(Object.entries(g)),E=m.next();!E.done;E=m.next())p=D(E.value,2),y=p[0],b=p[1],b==="mask"?v.maskSelector.push(y):b==="block"?v.blockSelector.push(y):b==="unmask"&&v.unmaskSelector.push(y)}catch(k){S={error:k}}finally{try{E&&!E.done&&(w=m.return)&&w.call(m)}finally{if(S)throw S.error}}a.privacyConfig=Ch(v,this.localConfig.loggerProvider)}return f&&Object.keys(f).length>0&&(a.targetingConfig=f),this.localConfig.loggerProvider.debug(JSON.stringify({name:"session replay joined config",config:hu(a)},null,2)),[2,{localConfig:this.localConfig,joinedConfig:a,remoteConfig:s}]}})})},t})(),Ph=function(t,e){return _(void 0,void 0,void 0,function(){var r,n;return T(this,function(i){return r=new Ah(t,e),n=new is(t,r.loggerProvider,r.serverZone,e.configServerUrl),[2,new kh(n,r)]})})},Me=Uint8Array,Oe=Uint16Array,cr=Uint32Array,Ri=new Me([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),xi=new Me([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),ia=new Me([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),yu=function(t,e){for(var r=new Oe(31),n=0;n<31;++n)r[n]=e+=1<>>1|(de&21845)<<1;it=(it&52428)>>>2|(it&13107)<<2,it=(it&61680)>>>4|(it&3855)<<4,ei[de]=((it&65280)>>>8|(it&255)<<8)>>>1}var or=(function(t,e,r){for(var n=t.length,i=0,o=new Oe(e);i>>l]=u}else for(s=new Oe(n),i=0;i>>15-t[i];return s}),St=new Me(288);for(var de=0;de<144;++de)St[de]=8;for(var de=144;de<256;++de)St[de]=9;for(var de=256;de<280;++de)St[de]=7;for(var de=280;de<288;++de)St[de]=8;var Jr=new Me(32);for(var de=0;de<32;++de)Jr[de]=5;var xh=or(St,9,0),Nh=or(Jr,5,0),Eu=function(t){return(t/8>>0)+(t&7&&1)},Su=function(t,e,r){(r==null||r>t.length)&&(r=t.length);var n=new(t instanceof Oe?Oe:t instanceof cr?cr:Me)(r-e);return n.set(t.subarray(e,r)),n},et=function(t,e,r){r<<=e&7;var n=e/8>>0;t[n]|=r,t[n+1]|=r>>>8},$t=function(t,e,r){r<<=e&7;var n=e/8>>0;t[n]|=r,t[n+1]|=r>>>8,t[n+2]|=r>>>16},Cn=function(t,e){for(var r=[],n=0;nd&&(d=o[n].s);var v=new Oe(d+1),h=ti(r[c-1],v,0);if(h>e){var n=0,g=0,m=h-e,E=1<e)g+=E-(1<>>=m;g>0;){var y=o[n].s;v[y]=0&&g;--n){var b=o[n].s;v[b]==e&&(--v[b],++g)}h=e}return[new Me(v),h]},ti=function(t,e,r){return t.s==-1?Math.max(ti(t.l,e,r+1),ti(t.r,e,r+1)):e[t.s]=r},aa=function(t){for(var e=t.length;e&&!t[--e];);for(var r=new Oe(++e),n=0,i=t[0],o=1,a=function(l){r[n++]=l},s=1;s<=e;++s)if(t[s]==i&&s!=e)++o;else{if(!i&&o>2){for(;o>138;o-=138)a(32754);o>2&&(a(o>10?o-11<<5|28690:o-3<<5|12305),o=0)}else if(o>3){for(a(i),--o;o>6;o-=6)a(8304);o>2&&(a(o-3<<5|8208),o=0)}for(;o--;)a(i);o=1,i=t[s]}return[r.subarray(0,n),e]},Xt=function(t,e){for(var r=0,n=0;n>>8,t[i+2]=t[i]^255,t[i+3]=t[i+1]^255;for(var o=0;o4&&!x[ia[R-1]];--R);var O=u+5<<3,L=Xt(i,St)+Xt(o,Jr)+a,B=Xt(i,d)+Xt(o,g)+a+14+3*R+Xt(A,x)+(2*A[16]+3*A[17]+7*A[18]);if(O<=L&&O<=B)return ri(e,c,t.subarray(l,l+u));var H,j,ee,M;if(et(e,c,1+(B15&&(et(e,c,J[I]>>>5&127),c+=J[I]>>>12)}}else H=xh,j=St,ee=Nh,M=Jr;for(var I=0;I255){var Z=n[I]>>>18&31;$t(e,c,H[Z+257]),c+=j[Z+257],Z>7&&(et(e,c,n[I]>>>23&31),c+=Ri[Z]);var K=n[I]&31;$t(e,c,ee[K]),c+=M[K],K>3&&($t(e,c,n[I]>>>5&8191),c+=xi[K])}else $t(e,c,H[n[I]]),c+=j[n[I]];return $t(e,c,H[256]),c+j[256]},Lh=new cr([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),Dh=function(t,e,r,n,i,o){var a=t.length,s=new Me(n+a+5*(1+Math.floor(a/7e3))+i),l=s.subarray(n,s.length-i),u=0;if(!e||a<8)for(var c=0;c<=a;c+=65535){var f=c+65535;f>>13,h=d&8191,g=(1<7e3||x>24576)&&H>423){u=sa(t,l,0,S,w,A,k,x,R,c-R,u),x=I=k=0,R=c;for(var j=0;j<286;++j)w[j]=0;for(var j=0;j<30;++j)A[j]=0}var ee=2,M=0,G=h,W=L-B&32767;if(H>2&&O==b(c-W))for(var te=Math.min(v,H)-1,J=Math.min(32767,c),Z=Math.min(258,H);W<=J&&--G&&L!=B;){if(t[c+ee]==t[c+ee-W]){for(var K=0;Kee){if(ee=K,M=W,K>te)break;for(var ie=Math.min(W,K-2),oe=0,j=0;joe&&(oe=me,B=fe)}}}L=B,B=m[L],W+=L-B+32768&32767}if(M){S[x++]=268435456|Zn[ee]<<18|oa[M];var N=Zn[ee]&31,he=oa[M]&31;k+=Ri[N]+xi[he],++w[257+N],++A[he],P=c+ee,++I}else S[x++]=t[c],++w[t[c]]}}u=sa(t,l,o,S,w,A,k,x,R,c-R,u)}return Su(s,0,n+Eu(u)+i)},Mh=function(){var t=1,e=0;return{p:function(r){for(var n=t,i=e,o=r.length,a=0;a!=o;){for(var s=Math.min(a+5552,o);a>>8<<16|(e&255)<<8|e>>>8)+((t&255)<<23)*2}}},Uh=function(t,e,r,n,i){return Dh(t,e.level==null?6:e.level,e.mem==null?Math.ceil(Math.max(8,Math.min(13,Math.log(t.length)))*1.5):12+e.mem,r,n,!0)},Fh=function(t,e,r){for(;r;++e)t[e]=r,r>>>=8},qh=function(t,e){var r=e.level,n=r==0?0:r<6?1:r==9?3:2;t[0]=120,t[1]=n<<6|(n?32-2*n:1)};function Bh(t,e){e===void 0&&(e={});var r=Mh();r.p(t);var n=Uh(t,e,2,4);return qh(n,e),Fh(n,n.length-4,r.d()),n}function Vh(t,e){var r=t.length;if(typeof TextEncoder<"u")return new TextEncoder().encode(t);for(var n=new Me(t.length+(t.length>>>1)),i=0,o=function(u){n[i++]=u},a=0;an.length){var s=new Me(i+8+(r-a<<1));s.set(n),n=s}var l=t.charCodeAt(a);l<128||e?o(l):l<2048?(o(192|l>>>6),o(128|l&63)):l>55295&&l<57344?(l=65536+(l&1047552)|t.charCodeAt(++a)&1023,o(240|l>>>18),o(128|l>>>12&63),o(128|l>>>6&63),o(128|l&63)):(o(224|l>>>12),o(128|l>>>6&63),o(128|l&63))}return Su(n,0,i)}function jh(t,e){for(var r="",n=0;n{const e={...t,v:Hh};return jh(Bh(Vh(JSON.stringify(e))))};var Wh=2e3,Kh=(function(){function t(e,r,n,i){var o=this,a;this.taskQueue=[],this.isProcessing=!1,this.compressEvent=function(f){var d=Gh(f);return JSON.stringify(d)},this.addCompressedEventToManager=function(f,d){o.eventsManager&&o.deviceId&&o.eventsManager.addEvent({event:{type:"replay",data:f},sessionId:d,deviceId:o.deviceId})},this.addCompressedEvent=function(f,d){if(o.worker)try{o.worker.postMessage({event:f,sessionId:d})}catch(h){h.name==="DataCloneError"?o.worker.postMessage(JSON.stringify({event:f,sessionId:d})):o.config.loggerProvider.warn("Unexpected error while posting message to worker:",h)}else{var v=o.compressEvent(f);o.addCompressedEventToManager(v,d)}},this.terminate=function(){var f;(f=o.worker)===null||f===void 0||f.terminate()};var s=F();if(this.canUseIdleCallback=s&&"requestIdleCallback"in s,this.eventsManager=e,this.config=r,this.deviceId=n,this.timeout=((a=r.performanceConfig)===null||a===void 0?void 0:a.timeout)||Wh,i){r.loggerProvider.log("Enabling web worker for compression");try{var l=new Blob([i],{type:"application/javascript"}),u=URL.createObjectURL(l),c=new Worker(u);c.onerror=function(f){f.preventDefault(),r.loggerProvider.error("Worker failed, falling back to non-worker compression:",f),c.terminate(),o.worker=void 0},c.onmessage=function(f){var d=f.data,v=d.compressedEvent,h=d.sessionId;o.addCompressedEventToManager(v,h)},this.worker=c}catch(f){r.loggerProvider.error("Failed to create worker, falling back to non-worker compression:",f)}}}return t.prototype.scheduleIdleProcessing=function(){var e=this;this.isProcessing||(this.isProcessing=!0,requestIdleCallback(function(r){e.processQueue(r)},{timeout:this.timeout}))},t.prototype.enqueueEvent=function(e,r){var n;this.canUseIdleCallback&&(!((n=this.config.performanceConfig)===null||n===void 0)&&n.enabled)?(this.config.loggerProvider.debug("Enqueuing event for processing during idle time."),this.taskQueue.push({event:e,sessionId:r}),this.scheduleIdleProcessing()):(this.config.loggerProvider.debug("Processing event without idle callback."),this.addCompressedEvent(e,r))},t.prototype.processQueue=function(e){for(var r=this;this.taskQueue.length>0&&(e.timeRemaining()>0||e.didTimeout);){var n=this.taskQueue.shift();if(n){var i=n.event,o=n.sessionId;this.addCompressedEvent(i,o)}}this.taskQueue.length>0?requestIdleCallback(function(a){r.processQueue(a)},{timeout:this.timeout}):this.isProcessing=!1},t})(),zh="Unexpected error occurred",$h="Network error occurred, event batch rejected",ua="Session replay event batch rejected due to exceeded retry count",Ct="Failed to store session replay events in IndexedDB",Xh="Session replay event batch not sent due to missing device ID",Yh="Session replay event batch not sent due to missing api key",ni="1.35.1",Jh=(function(){function t(e){var r=e.trackServerUrl,n=e.loggerProvider,i=e.payloadBatcher;this.storageKey="",this.retryTimeout=1e3,this.scheduled=null,this.queue=[],this.loggerProvider=n,this.payloadBatcher=i||function(o){return o},this.trackServerUrl=r}return t.prototype.sendEventsList=function(e){this.addToQueue(C(C({},e),{attempts:0,timeout:0}))},t.prototype.addToQueue=function(){for(var e=this,r=[],n=0;n0&&r.schedule(e)})},e))},t.prototype.flush=function(e){return e===void 0&&(e=!1),_(this,void 0,void 0,function(){var r,n,i,o,a,s,l;return T(this,function(u){switch(u.label){case 0:r=this.queue,this.queue=[],this.scheduled&&(clearTimeout(this.scheduled),this.scheduled=null),u.label=1;case 1:u.trys.push([1,6,7,8]),n=q(r),i=n.next(),u.label=2;case 2:return i.done?[3,5]:(o=i.value,[4,this.send(o,e)]);case 3:u.sent(),u.label=4;case 4:return i=n.next(),[3,2];case 5:return[3,8];case 6:return a=u.sent(),s={error:a},[3,8];case 7:try{i&&!i.done&&(l=n.return)&&l.call(n)}finally{if(s)throw s.error}return[7];case 8:return[2]}})})},t.prototype.send=function(e,r){var n,i;return r===void 0&&(r=!0),_(this,void 0,void 0,function(){var o,a,s,l,u,c,f,d,v,h,g,m,E;return T(this,function(p){switch(p.label){case 0:if(o=e.apiKey,!o)return[2,this.completeRequest({context:e,err:Yh})];if(a=e.deviceId,!a)return[2,this.completeRequest({context:e,err:Xh})];if(s=Sh(),l=ni,u=e.sampleRate,c=new URLSearchParams({device_id:a,session_id:"".concat(e.sessionId),type:"".concat(e.type)}),f="".concat(((n=e.version)===null||n===void 0?void 0:n.type)||"standalone","/").concat(((i=e.version)===null||i===void 0?void 0:i.version)||l),d=this.payloadBatcher({version:1,events:e.events}),d.events.length===0)return this.completeRequest({context:e}),[2];p.label=1;case 1:return p.trys.push([1,6,,7]),v={headers:{"Content-Type":"application/json",Accept:"*/*",Authorization:"Bearer ".concat(o),"X-Client-Version":l,"X-Client-Library":f,"X-Client-Url":s.substring(0,mh),"X-Client-Sample-Rate":"".concat(u),"X-Sampling-Hash-Alg":"xxhash32"},body:JSON.stringify(d),method:"POST"},h="".concat(Oi(e.serverZone,this.trackServerUrl),"?").concat(c.toString()),[4,fetch(h,v)];case 2:if(g=p.sent(),g===null)return this.completeRequest({context:e,err:zh}),[2];if(r)return[3,3];m="";try{m=JSON.stringify(g.body,null,2)}catch{}return this.completeRequest({context:e,success:"".concat(g.status,": ").concat(m)}),[3,5];case 3:return[4,this.handleReponse(g.status,e)];case 4:p.sent(),p.label=5;case 5:return[3,7];case 6:return E=p.sent(),this.completeRequest({context:e,err:E}),[3,7];case 7:return[2]}})})},t.prototype.handleReponse=function(e,r){return _(this,void 0,void 0,function(){var n,i;return T(this,function(o){switch(o.label){case 0:switch(n=new dr().buildStatus(e),i=n,i){case pe.Success:return[3,1];case pe.Failed:return[3,2]}return[3,4];case 1:return this.handleSuccessResponse(r),[3,5];case 2:return[4,this.handleOtherResponse(r)];case 3:return o.sent(),[3,5];case 4:this.completeRequest({context:r,err:$h}),o.label=5;case 5:return[2]}})})},t.prototype.handleSuccessResponse=function(e){var r=Math.round(new Blob(e.events).size/cu);this.completeRequest({context:e,success:"Session replay event batch tracked successfully for session id ".concat(e.sessionId,", size of events: ").concat(r," KB")})},t.prototype.handleOtherResponse=function(e){return _(this,void 0,void 0,function(){var r;return T(this,function(n){switch(n.label){case 0:return r=e.attempts*this.retryTimeout,e.attempts++,e.attempts>(e.flushMaxRetries||0)?(this.completeRequest({context:e,err:ua}),[2]):[4,new Promise(function(i){return setTimeout(i,r)})];case 1:return n.sent(),[4,this.send(e,!0)];case 2:return n.sent(),[2]}})})},t.prototype.completeRequest=function(e){var r=e.context,n=e.err,i=e.success;r.onComplete(),n?this.loggerProvider.warn(n):i&&this.loggerProvider.log(i)},t})();const ii=(t,e)=>e.some(r=>t instanceof r);let la,ca;function Qh(){return la||(la=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function Zh(){return ca||(ca=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const oi=new WeakMap,kn=new WeakMap,an=new WeakMap;function eg(t){const e=new Promise((r,n)=>{const i=()=>{t.removeEventListener("success",o),t.removeEventListener("error",a)},o=()=>{r(bt(t.result)),i()},a=()=>{n(t.error),i()};t.addEventListener("success",o),t.addEventListener("error",a)});return an.set(e,t),e}function tg(t){if(oi.has(t))return;const e=new Promise((r,n)=>{const i=()=>{t.removeEventListener("complete",o),t.removeEventListener("error",a),t.removeEventListener("abort",a)},o=()=>{r(),i()},a=()=>{n(t.error||new DOMException("AbortError","AbortError")),i()};t.addEventListener("complete",o),t.addEventListener("error",a),t.addEventListener("abort",a)});oi.set(t,e)}let ai={get(t,e,r){if(t instanceof IDBTransaction){if(e==="done")return oi.get(t);if(e==="store")return r.objectStoreNames[1]?void 0:r.objectStore(r.objectStoreNames[0])}return bt(t[e])},set(t,e,r){return t[e]=r,!0},has(t,e){return t instanceof IDBTransaction&&(e==="done"||e==="store")?!0:e in t}};function wu(t){ai=t(ai)}function rg(t){return Zh().includes(t)?function(...e){return t.apply(si(this),e),bt(this.request)}:function(...e){return bt(t.apply(si(this),e))}}function ng(t){return typeof t=="function"?rg(t):(t instanceof IDBTransaction&&tg(t),ii(t,Qh())?new Proxy(t,ai):t)}function bt(t){if(t instanceof IDBRequest)return eg(t);if(kn.has(t))return kn.get(t);const e=ng(t);return e!==t&&(kn.set(t,e),an.set(e,t)),e}const si=t=>an.get(t);function Tu(t,e,{blocked:r,upgrade:n,blocking:i,terminated:o}={}){const a=indexedDB.open(t,e),s=bt(a);return n&&a.addEventListener("upgradeneeded",l=>{n(bt(a.result),l.oldVersion,l.newVersion,bt(a.transaction),l)}),r&&a.addEventListener("blocked",l=>r(l.oldVersion,l.newVersion,l)),s.then(l=>{o&&l.addEventListener("close",()=>o()),i&&l.addEventListener("versionchange",u=>i(u.oldVersion,u.newVersion,u))}).catch(()=>{}),s}const ig=["get","getKey","getAll","getAllKeys","count"],og=["put","add","delete","clear"],Pn=new Map;function da(t,e){if(!(t instanceof IDBDatabase&&!(e in t)&&typeof e=="string"))return;if(Pn.get(e))return Pn.get(e);const r=e.replace(/FromIndex$/,""),n=e!==r,i=og.includes(r);if(!(r in(n?IDBIndex:IDBObjectStore).prototype)||!(i||ig.includes(r)))return;const o=async function(a,...s){const l=this.transaction(a,i?"readwrite":"readonly");let u=l.store;return n&&(u=u.index(s.shift())),(await Promise.all([u[r](...s),i&&l.done]))[0]};return Pn.set(e,o),o}wu(t=>({...t,get:(e,r,n)=>da(e,r)||t.get(e,r,n),has:(e,r)=>!!da(e,r)||t.has(e,r)}));const ag=["continue","continuePrimaryKey","advance"],fa={},ui=new WeakMap,_u=new WeakMap,sg={get(t,e){if(!ag.includes(e))return t[e];let r=fa[e];return r||(r=fa[e]=function(...n){ui.set(this,_u.get(this)[e](...n))}),r}};async function*ug(...t){let e=this;if(e instanceof IDBCursor||(e=await e.openCursor(...t)),!e)return;e=e;const r=new Proxy(e,sg);for(_u.set(r,e),an.set(r,si(e));e;)yield r,e=await(ui.get(r)||e.continue()),ui.delete(r)}function va(t,e){return e===Symbol.asyncIterator&&ii(t,[IDBIndex,IDBObjectStore,IDBCursor])||e==="iterate"&&ii(t,[IDBIndex,IDBObjectStore])}wu(t=>({...t,get(e,r,n){return va(e,r)?ug:t.get(e,r,n)},has(e,r){return va(e,r)||t.has(e,r)}}));var Iu=(function(){function t(e){var r=this,n,i,o;this.minInterval=gh,this.maxInterval=ph,this.maxPersistedEventsSize=fh,this.interval=this.minInterval,this._timeAtLastSplit=Date.now(),this.shouldSplitEventsList=function(a,s){var l=r.getStringSize(s),u=r.getEventsArraySize(a);return u+l>=r.maxPersistedEventsSize?!0:Date.now()-r.timeAtLastSplit>r.interval&&a.length?(r.interval=Math.min(r.maxInterval,r.interval+r.minInterval),r._timeAtLastSplit=Date.now(),!0):!1},this.loggerProvider=e.loggerProvider,this.minInterval=(n=e.minInterval)!==null&&n!==void 0?n:this.minInterval,this.maxInterval=(i=e.maxInterval)!==null&&i!==void 0?i:this.maxInterval,this.maxPersistedEventsSize=(o=e.maxPersistedEventsSize)!==null&&o!==void 0?o:this.maxPersistedEventsSize}return Object.defineProperty(t.prototype,"timeAtLastSplit",{get:function(){return this._timeAtLastSplit},enumerable:!1,configurable:!0}),t.prototype.getStringSize=function(e){return e.length},t.prototype.getEventsArraySize=function(e){var r,n,i=0;try{for(var o=q(e),a=o.next();!a.done;a=o.next()){var s=a.value;i+=this.getStringSize(s)}}catch(u){r={error:u}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}var l=2+Math.max(0,e.length-1)+e.length*2;return i+l},t})(),lg=function(t){return typeof t=="object"&&t!==null&&t.name==="AbortError"},tt=function(t,e,r){lg(r)?t.debug(e):t.warn(e)},ar="sessionCurrentSequence",sr="sequencesToSend",cg=function(t){var e,r;return t.objectStoreNames.contains(ar)||(r=t.createObjectStore(ar,{keyPath:"sessionId"})),t.objectStoreNames.contains(sr)||(e=t.createObjectStore(sr,{keyPath:"sequenceId",autoIncrement:!0}),e.createIndex("sessionId","sessionId")),{sequencesStore:e,currentSequenceStore:r}},dg=function(t){return _(void 0,void 0,void 0,function(){return T(this,function(e){switch(e.label){case 0:return[4,Tu(t,1,{upgrade:cg})];case 1:return[2,e.sent()]}})})},fg=(function(t){Fe(e,t);function e(r){var n=t.call(this,r)||this;return n.getSequencesToSend=function(){return _(n,void 0,void 0,function(){var i,o,a,s,l,u;return T(this,function(c){switch(c.label){case 0:return c.trys.push([0,5,,6]),i=[],[4,this.db.transaction("sequencesToSend").store.openCursor()];case 1:o=c.sent(),c.label=2;case 2:return o?(a=o.value,s=a.sessionId,l=a.events,i.push({events:l,sequenceId:o.key,sessionId:s}),[4,o.continue()]):[3,4];case 3:return o=c.sent(),[3,2];case 4:return[2,i];case 5:return u=c.sent(),tt(this.loggerProvider,"".concat(Ct,": ").concat(u),u),[3,6];case 6:return[2,void 0]}})})},n.storeCurrentSequence=function(i){return _(n,void 0,void 0,function(){var o,a,s;return T(this,function(l){switch(l.label){case 0:return l.trys.push([0,4,,5]),[4,this.db.get(ar,i)];case 1:return o=l.sent(),o?[4,this.db.put(sr,{sessionId:i,events:o.events})]:[2,void 0];case 2:return a=l.sent(),[4,this.db.put(ar,{sessionId:i,events:[]})];case 3:return l.sent(),[2,C(C({},o),{sessionId:i,sequenceId:a})];case 4:return s=l.sent(),tt(this.loggerProvider,"".concat(Ct,": ").concat(s),s),[3,5];case 5:return[2,void 0]}})})},n.addEventToCurrentSequence=function(i,o){return _(n,void 0,void 0,function(){var a,s,l,u,c,f;return T(this,function(d){switch(d.label){case 0:return d.trys.push([0,10,,11]),a=this.db.transaction(ar,"readwrite"),[4,a.store.get(i)];case 1:return s=d.sent(),s?[3,3]:[4,a.store.put({sessionId:i,events:[o]})];case 2:return d.sent(),[2];case 3:return l=void 0,this.shouldSplitEventsList(s.events,o)?(l=s.events,[4,a.store.put({sessionId:i,events:[o]})]):[3,5];case 4:return d.sent(),[3,7];case 5:return u=s.events.concat(o),[4,a.store.put({sessionId:i,events:u})];case 6:d.sent(),d.label=7;case 7:return[4,a.done];case 8:return d.sent(),l?[4,this.storeSendingEvents(i,l)]:[2,void 0];case 9:return c=d.sent(),c?[2,{events:l,sessionId:i,sequenceId:c}]:[2,void 0];case 10:return f=d.sent(),tt(this.loggerProvider,"".concat(Ct,": ").concat(f),f),[3,11];case 11:return[2,void 0]}})})},n.storeSendingEvents=function(i,o){return _(n,void 0,void 0,function(){var a,s;return T(this,function(l){switch(l.label){case 0:return l.trys.push([0,2,,3]),[4,this.db.put(sr,{sessionId:i,events:o})];case 1:return a=l.sent(),[2,a];case 2:return s=l.sent(),tt(this.loggerProvider,"".concat(Ct,": ").concat(s),s),[3,3];case 3:return[2,void 0]}})})},n.cleanUpSessionEventsStore=function(i,o){return _(n,void 0,void 0,function(){var a;return T(this,function(s){switch(s.label){case 0:if(!o)return[2];s.label=1;case 1:return s.trys.push([1,3,,4]),[4,this.db.delete(sr,o)];case 2:return s.sent(),[3,4];case 3:return a=s.sent(),tt(this.loggerProvider,"".concat(Ct,": ").concat(a),a),[3,4];case 4:return[2]}})})},n.db=r.db,n}return e.new=function(r,n){return _(this,void 0,void 0,function(){var i,o,a,s;return T(this,function(l){switch(l.label){case 0:return l.trys.push([0,2,,3]),i=r==="replay"?"":"_".concat(r),o="".concat(n.apiKey.substring(0,10),"_amp_session_replay_events").concat(i),[4,dg(o)];case 1:return a=l.sent(),[2,new e(C(C({},n),{db:a}))];case 2:return s=l.sent(),tt(n.loggerProvider,"".concat(Ct,": ").concat(s),s),[3,3];case 3:return[2]}})})},e.prototype.getCurrentSequenceEvents=function(r){return _(this,void 0,void 0,function(){var a,n,i,o,a,s,l,u;return T(this,function(c){switch(c.label){case 0:return r?[4,this.db.get("sessionCurrentSequence",r)]:[3,2];case 1:return a=c.sent(),a?[2,[a]]:[2,void 0];case 2:n=[],c.label=3;case 3:return c.trys.push([3,8,9,10]),[4,this.db.getAll("sessionCurrentSequence")];case 4:i=q.apply(void 0,[c.sent()]),o=i.next(),c.label=5;case 5:if(o.done)return[3,7];a=o.value,n.push(a),c.label=6;case 6:return o=i.next(),[3,5];case 7:return[3,10];case 8:return s=c.sent(),l={error:s},[3,10];case 9:try{o&&!o.done&&(u=i.return)&&u.call(i)}finally{if(l)throw l.error}return[7];case 10:return[2,n]}})})},e})(Iu),vg=(function(t){Fe(e,t);function e(){var r=t!==null&&t.apply(this,arguments)||this;return r.finalizedSequences={},r.sequences={},r.sequenceId=0,r}return e.prototype.resetCurrentSequence=function(r){this.sequences[r]=[]},e.prototype.addSequence=function(r){var n=this.sequenceId++,i=$([],D(this.sequences[r]),!1);return this.finalizedSequences[n]={sessionId:r,events:i},this.resetCurrentSequence(r),{sequenceId:n,events:i,sessionId:r}},e.prototype.getSequencesToSend=function(){return _(this,void 0,void 0,function(){return T(this,function(r){return[2,Object.entries(this.finalizedSequences).map(function(n){var i=D(n,2),o=i[0],a=i[1],s=a.sessionId,l=a.events;return{sequenceId:Number(o),sessionId:s,events:l}})]})})},e.prototype.storeCurrentSequence=function(r){return _(this,void 0,void 0,function(){return T(this,function(n){return this.sequences[r]?[2,this.addSequence(r)]:[2,void 0]})})},e.prototype.addEventToCurrentSequence=function(r,n){return _(this,void 0,void 0,function(){var i;return T(this,function(o){return this.sequences[r]||this.resetCurrentSequence(r),this.shouldSplitEventsList(this.sequences[r],n)&&(i=this.addSequence(r)),this.sequences[r].push(n),[2,i]})})},e.prototype.storeSendingEvents=function(r,n){return _(this,void 0,void 0,function(){return T(this,function(i){return this.finalizedSequences[this.sequenceId]={sessionId:r,events:n},[2,this.sequenceId++]})})},e.prototype.cleanUpSessionEventsStore=function(r,n){return _(this,void 0,void 0,function(){return T(this,function(i){return n!==void 0&&delete this.finalizedSequences[n],[2]})})},e})(Iu),ha=function(t){var e=t.config,r=t.minInterval,n=t.maxInterval,i=t.type,o=t.payloadBatcher,a=t.storeType;return _(void 0,void 0,void 0,function(){function s(E){return E===void 0&&(E=!1),_(this,void 0,void 0,function(){return T(this,function(p){return[2,l.flush(E)]})})}var l,u,c,f,d,v,h,g,m;return T(this,function(E){switch(E.label){case 0:return l=new Jh(C(C({},e),{loggerProvider:e.loggerProvider,payloadBatcher:o})),u=function(){return new vg({loggerProvider:e.loggerProvider,maxInterval:n,minInterval:r})},c=function(){return _(void 0,void 0,void 0,function(){var p;return T(this,function(y){switch(y.label){case 0:return[4,fg.new(i,{loggerProvider:e.loggerProvider,minInterval:r,maxInterval:n,apiKey:e.apiKey})];case 1:return p=y.sent(),p?[2,p]:(e.loggerProvider.log("Failed to initialize idb store, falling back to memory store."),[2,u()])}})})},a!=="idb"?[3,2]:[4,c()];case 1:return d=E.sent(),[3,3];case 2:d=u(),E.label=3;case 3:return f=d,v=function(p){var y=p.events,b=p.sessionId,S=p.deviceId,w=p.sequenceId;e.debugMode&&vu().then(function(A){var I=A.totalStorageSize,k=A.percentOfQuota,x=A.usageDetails;e.loggerProvider.debug("Total storage size: ".concat(I," KB, percentage of quota: ").concat(k,"%, usage details: ").concat(x))}).catch(function(){}),l.sendEventsList({events:y,sessionId:b,flushMaxRetries:e.flushMaxRetries,apiKey:e.apiKey,deviceId:S,sampleRate:e.sampleRate,serverZone:e.serverZone,version:e.version,type:i,onComplete:function(){return _(void 0,void 0,void 0,function(){return T(this,function(A){switch(A.label){case 0:return[4,f.cleanUpSessionEventsStore(b,w)];case 1:return A.sent(),[2]}})})}})},h=function(p){var y=p.sessionId,b=p.deviceId;f.storeCurrentSequence(y).then(function(S){S&&v({sequenceId:S.sequenceId,events:S.events,sessionId:S.sessionId,deviceId:b})}).catch(function(S){e.loggerProvider.warn("Failed to get current sequence of session replay events for session:",S)})},g=function(p){var y=p.deviceId;return _(void 0,void 0,void 0,function(){var b;return T(this,function(S){switch(S.label){case 0:return[4,f.getSequencesToSend()];case 1:return b=S.sent(),b&&b.forEach(function(w){v({sequenceId:w.sequenceId,events:w.events,sessionId:w.sessionId,deviceId:y})}),[2]}})})},m=function(p){var y=p.event,b=p.sessionId,S=p.deviceId;f.addEventToCurrentSequence(b,y.data).then(function(w){return w&&v({sequenceId:w.sequenceId,events:w.events,sessionId:w.sessionId,deviceId:S})}).catch(function(w){e.loggerProvider.warn("Failed to add event to session replay capture:",w)})},[2,{sendCurrentSequenceEvents:h,addEvent:m,sendStoredEvents:g,flush:s}]}})})},ga=(function(){function t(){for(var e=[],r=0;r0&&(n=i[0]),sn(n)}else throw new Error("Selector was not found.")}function gg(t,e){return t.nodeType===Node.DOCUMENT_NODE?t:t===e.root?t.ownerDocument:t}function Ir(t,e,r){for(var n=null,i=[],o=t,a=0,s=function(){var u,c,f=new Date().getTime()-Au.getTime();if(Ue.timeoutMs!==void 0&&f>Ue.timeoutMs)throw new Error("Timeout: Can't find a unique selector after ".concat(f,"ms"));var d=Cr(pg(o))||Cr.apply(void 0,$([],D(mg(o)),!1))||Cr.apply(void 0,$([],D(yg(o)),!1))||Cr(bg(o))||[ya()],v=Eg(o);if(e=="all")v&&(d=d.concat(d.filter(On).map(function(p){return Ar(p,v)})));else if(e=="two")d=d.slice(0,1),v&&(d=d.concat(d.filter(On).map(function(p){return Ar(p,v)})));else if(e=="one"){var h=D(d=d.slice(0,1),1),g=h[0];v&&On(g)&&(d=[Ar(g,v)])}else e=="none"&&(d=[ya()],v&&(d=[Ar(d[0],v)]));try{for(var m=(u=void 0,q(d)),E=m.next();!E.done;E=m.next()){var g=E.value;g.level=a}}catch(p){u={error:p}}finally{try{E&&!E.done&&(c=m.return)&&c.call(m)}finally{if(u)throw u.error}}if(i.push(d),i.length>=Ue.seedMinLength&&(n=pa(i,r),n))return"break";o=o.parentElement,a++};o;){var l=s();if(l==="break")break}return n||(n=pa(i,r)),!n&&r?r():n}function pa(t,e){var r,n,i=Pu(ku(t));if(i.length>Ue.threshold)return e?e():null;try{for(var o=q(i),a=o.next();!a.done;a=o.next()){var s=a.value;if(Cu(s))return s}}catch(l){r={error:l}}finally{try{a&&!a.done&&(n=o.return)&&n.call(o)}finally{if(r)throw r.error}}return null}function sn(t){for(var e=t[0],r=e.name,n=1;n ").concat(r):r="".concat(t[n].name," ").concat(r),e=t[n]}return r}function ma(t){return t.map(function(e){return e.penalty}).reduce(function(e,r){return e+r},0)}function Cu(t){var e=sn(t);switch(Ni.querySelectorAll(e).length){case 0:throw new Error("Can't select any node with this selector: ".concat(e));case 1:return!0;default:return!1}}function pg(t){var e=t.getAttribute("id");return e&&Ue.idName(e)?{name:"#"+CSS.escape(e),penalty:0}:null}function mg(t){var e=Array.from(t.attributes).filter(function(r){return Ue.attr(r.name,r.value)});return e.map(function(r){return{name:"[".concat(CSS.escape(r.name),'="').concat(CSS.escape(r.value),'"]'),penalty:.5}})}function yg(t){var e=Array.from(t.classList).filter(Ue.className);return e.map(function(r){return{name:"."+CSS.escape(r),penalty:1}})}function bg(t){var e=t.tagName.toLowerCase();return Ue.tagName(e)?{name:e,penalty:2}:null}function ya(){return{name:"*",penalty:3}}function Eg(t){var e=t.parentNode;if(!e)return null;var r=e.firstChild;if(!r)return null;for(var n=0;r&&(r.nodeType===Node.ELEMENT_NODE&&n++,r!==t);)r=r.nextSibling;return n}function Ar(t,e){return{name:t.name+":nth-child(".concat(e,")"),penalty:t.penalty+1}}function On(t){return t.name!=="html"&&!t.name.startsWith("#")}function Cr(){for(var t=[],e=0;e0?r:null}function Sg(t){return t!=null}function ku(t,e){var r,n,i,o,a,s;return e===void 0&&(e=[]),T(this,function(l){switch(l.label){case 0:if(!(t.length>0))return[3,9];l.label=1;case 1:l.trys.push([1,6,7,8]),r=q(t[0]),n=r.next(),l.label=2;case 2:return n.done?[3,5]:(i=n.value,[5,q(ku(t.slice(1,t.length),e.concat(i)))]);case 3:l.sent(),l.label=4;case 4:return n=r.next(),[3,2];case 5:return[3,8];case 6:return o=l.sent(),a={error:o},[3,8];case 7:try{n&&!n.done&&(s=r.return)&&s.call(r)}finally{if(a)throw a.error}return[7];case 8:return[3,11];case 9:return[4,e];case 10:l.sent(),l.label=11;case 11:return[2]}})}function Pu(t){return $([],D(t),!1).sort(function(e,r){return ma(e)-ma(r)})}function Ou(t,e,r){var n,i,o;return r===void 0&&(r={counter:0,visited:new Map}),T(this,function(a){switch(a.label){case 0:if(!(t.length>2&&t.length>Ue.optimizedMinLength))return[3,5];n=1,a.label=1;case 1:return nUe.maxNumberOfTries?[2]:(r.counter+=1,i=$([],D(t),!1),i.splice(n,1),o=sn(i),r.visited.has(o)?[2]:Cu(i)&&wg(i,e)?[4,i]:[3,4]):[3,5];case 2:return a.sent(),r.visited.set(o,!0),[5,q(Ou(i,e,r))];case 3:a.sent(),a.label=4;case 4:return n++,[3,1];case 5:return[2]}})}function wg(t,e){return Ni.querySelector(sn(t))===e}var Tg=36e5,_g=function(t){var e=t.version,r=t.events,n=[];return r.forEach(function(i){var o=JSON.parse(i);o.count=1,o.type==="click"&&n.push(o)}),{version:e,events:n}},Ig=function(t){var e=t.version,r=t.events,n=[];r.forEach(function(o){var a=JSON.parse(o);a.type==="click"&&n.push(a)});var i=n.reduce(function(o,a){var s=a.x,l=a.y,u=a.selector,c=a.timestamp,f=c-c%Tg,d="".concat(s,":").concat(l,":").concat(u??"",":").concat(f);return o[d]?o[d].count+=1:o[d]=C(C({},a),{timestamp:f,count:1}),o},{});return{version:e,events:Object.values(i)}},Ag=(function(){function t(e,r){var n=this;this.createHook=function(i){var o=i.eventsManager,a=i.sessionId,s=i.deviceIdFn,l=i.mirror,u=i.ugcFilterRules,c=i.performanceOptions;return function(f){if(f.type===mu.Click){var d=F();if(d){var v=d.location,h=d.innerHeight,g=d.innerWidth;if(v){var m=f.x,E=f.y;if(!(m===void 0||E===void 0)){var p=l.getNode(f.id),y;if(p)try{y=hg(p,c)}catch{n.logger.debug("error resolving selector from finder")}var b=on(v.href,u),S={x:m+n.scrollWatcher.currentScrollX,y:E+n.scrollWatcher.currentScrollY,selector:y,viewportHeight:h,viewportWidth:g,pageUrl:b,timestamp:Date.now(),type:"click"},w=s();w&&o.addEvent({sessionId:a,event:{type:"interaction",data:JSON.stringify(S)},deviceId:w})}}}}}},this.logger=e,this.scrollWatcher=r}return t})();function Rn(){var t=F();return t?.innerHeight||document.documentElement&&document.documentElement.clientHeight||0}function xn(){var t=F();return t?.innerWidth||document.documentElement&&document.documentElement.clientWidth||0}var Cg=(function(){function t(e,r){var n=F();n&&n.navigator&&typeof n.navigator.sendBeacon=="function"?this.sendBeacon=function(i,o){try{if(n.navigator.sendBeacon(i,JSON.stringify(o)))return!0}catch{}return!1}:this.sendBeacon=function(){return!1},this.sendXhr=function(i,o){var a=new XMLHttpRequest;return a.open("POST",i,!0),a.setRequestHeader("Accept","*/*"),a.send(JSON.stringify(o)),!0},this.basePageUrl=Oi(r.serverZone,r.trackServerUrl),this.apiKey=r.apiKey,this.context=e}return t.prototype.send=function(e,r){var n=this.context,i=n.sessionId,o=n.type,a=new URLSearchParams({device_id:e,session_id:String(i),type:String(o),api_key:this.apiKey}),s="".concat(this.basePageUrl,"?").concat(a.toString());this.sendBeacon(s,r)||this.sendXhr(s,r)},t})(),kg=(function(){function t(e,r){var n=this;this.timestamp=Date.now(),this.hook=function(i){n.update(i)},this.send=function(i){return function(o){var a,s,l,u,c=i(),f=F();if(f&&c){var d=(a=f.scrollX)!==null&&a!==void 0?a:0,v=(s=f.scrollY)!==null&&s!==void 0?s:0;(d>0||v>0)&&n.update({id:1,x:d,y:v}),n.transport.send(c,{version:1,events:[{maxScrollX:n._maxScrollX,maxScrollY:n._maxScrollY,maxScrollWidth:n._maxScrollWidth,maxScrollHeight:n._maxScrollHeight,viewportHeight:Rn(),viewportWidth:xn(),pageUrl:on(f.location.href,(u=(l=n.config.interactionConfig)===null||l===void 0?void 0:l.ugcFilterRules)!==null&&u!==void 0?u:[]),timestamp:n.timestamp,type:"scroll"}]})}}},this._maxScrollX=0,this._maxScrollY=0,this._currentScrollX=0,this._currentScrollY=0,this._maxScrollWidth=xn(),this._maxScrollHeight=Rn(),this.config=r,this.transport=e}return t.default=function(e,r){return new t(new Cg(e,r),r)},Object.defineProperty(t.prototype,"maxScrollX",{get:function(){return this._maxScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"maxScrollY",{get:function(){return this._maxScrollY},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"maxScrollWidth",{get:function(){return this._maxScrollWidth},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"maxScrollHeight",{get:function(){return this._maxScrollHeight},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"currentScrollX",{get:function(){return this._currentScrollX},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"currentScrollY",{get:function(){return this._currentScrollY},enumerable:!1,configurable:!0}),t.prototype.update=function(e){var r=Date.now();if(this._currentScrollX=e.x,this._currentScrollY=e.y,e.x>this._maxScrollX){var n=xn();this._maxScrollX=e.x;var i=e.x+n;i>this._maxScrollWidth&&(this._maxScrollWidth=i),this.timestamp=r}if(e.y>this._maxScrollY){var o=Rn();this._maxScrollY=e.y;var a=e.y+o;a>this._maxScrollHeight&&(this._maxScrollHeight=a),this.timestamp=r}},t})(),ba=(function(){function t(e){var r=e.sessionId,n=e.deviceId;this.deviceId=n,this.sessionId=r,r&&n&&(this.sessionReplayId=wh(r,n))}return t})(),Pg=1e3*60*60*24*2,Og=(function(){function t(){var e=this;this.dbs={},this.createStore=function(r){return _(e,void 0,void 0,function(){return T(this,function(n){switch(n.label){case 0:return[4,Tu(r,1,{upgrade:function(i){i.objectStoreNames.contains("sessionTargetingMatch")||i.createObjectStore("sessionTargetingMatch",{keyPath:"sessionId"})}})];case 1:return[2,n.sent()]}})})},this.openOrCreateDB=function(r){return _(e,void 0,void 0,function(){var n,i;return T(this,function(o){switch(o.label){case 0:return this.dbs&&this.dbs[r]?[2,this.dbs[r]]:(n="".concat(r.substring(0,10),"_amp_session_replay_targeting"),[4,this.createStore(n)]);case 1:return i=o.sent(),this.dbs[r]=i,[2,i]}})})},this.getTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId;return _(e,void 0,void 0,function(){var a,s,l,u;return T(this,function(c){switch(c.label){case 0:return c.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return a=c.sent(),s=String(o),[4,a.get("sessionTargetingMatch",s)];case 2:return l=c.sent(),[2,l?.targetingMatch];case 3:return u=c.sent(),tt(n,"Failed to get targeting match for session id ".concat(o,": ").concat(u),u),[3,4];case 4:return[2,void 0]}})})},this.storeTargetingMatchForSession=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.sessionId,a=r.targetingMatch;return _(e,void 0,void 0,function(){var s,l,u,c;return T(this,function(f){switch(f.label){case 0:return f.trys.push([0,3,,4]),[4,this.openOrCreateDB(i)];case 1:return s=f.sent(),l=String(o),[4,s.put("sessionTargetingMatch",{targetingMatch:a,sessionId:l,lastUpdated:Date.now()})];case 2:return u=f.sent(),[2,u];case 3:return c=f.sent(),tt(n,"Failed to store targeting match for session id ".concat(o,": ").concat(c),c),[3,4];case 4:return[2,void 0]}})})},this.clearStoreOfOldSessions=function(r){var n=r.loggerProvider,i=r.apiKey,o=r.currentSessionId;return _(e,void 0,void 0,function(){var a,s,l,u,c,f,d,v;return T(this,function(h){switch(h.label){case 0:return h.trys.push([0,8,,9]),[4,this.openOrCreateDB(i)];case 1:return a=h.sent(),s=String(o),l=a.transaction("sessionTargetingMatch","readwrite"),[4,l.store.getAll()];case 2:u=h.sent(),c=0,h.label=3;case 3:return cPg?[4,l.store.delete(f.sessionId)]:[3,5]):[3,6];case 4:h.sent(),h.label=5;case 5:return c++,[3,3];case 6:return[4,l.done];case 7:return h.sent(),[3,9];case 8:return v=h.sent(),tt(n,"Failed to clear old targeting matches for sessions: ".concat(v),v),[3,9];case 9:return[2]}})})}}return t})(),Nn=new Og,Rg=function(t){var e=t.sessionId,r=t.targetingConfig,n=t.loggerProvider,i=t.apiKey,o=t.targetingParams,a=t.urlChange,s=a===void 0?!1:a;return _(void 0,void 0,void 0,function(){var l,u,c,f,d,v,h;return T(this,function(g){switch(g.label){case 0:return[4,Nn.clearStoreOfOldSessions({loggerProvider:n,apiKey:i,currentSessionId:e})];case 1:return g.sent(),[4,Nn.getTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:e})];case 2:if(l=g.sent(),l===!0&&!s)return[2,!0];u=!0,g.label=3;case 3:return g.trys.push([3,6,,7]),[4,Qt(()=>import("./DqeYcxy-.js"),[])];case 4:return c=g.sent().evaluateTargeting,f=C(C({},o),{flag:r,sessionId:typeof e=="string"?parseInt(e,10):e,apiKey:i,loggerProvider:n}),[4,c(f)];case 5:return d=g.sent(),d&&d.sr_targeting_config&&(u=d.sr_targeting_config.key==="on"),Nn.storeTargetingMatchForSession({loggerProvider:n,apiKey:i,sessionId:e,targetingMatch:u}),[3,7];case 6:return v=g.sent(),h=v,n.warn(h.message),[3,7];case 7:return[2,u]}})})},Dr=2654435761,Mr=2246822519,Ea=3266489917,xg=668265263,Sa=374761393;function pt(t,e){return(t<>>32-e)>>>0}function kr(t,e){return t=t+Math.imul(e,Mr)>>>0,t=pt(t,13),t=Math.imul(t,Dr)>>>0,t}function Yt(t,e){return(t[e]|t[e+1]<<8|t[e+2]<<16|t[e+3]<<24)>>>0}function Ng(t){for(var e=[],r=0;r=55296&&n<=56319&&r+1=56320&&i<=57343&&(n=(n-55296<<10)+(i-56320)+65536,r++)}n<128?e.push(n):n<2048?e.push(192|n>>6,128|n&63):n<65536?e.push(224|n>>12,128|n>>6&63,128|n&63):e.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|n&63)}return new Uint8Array(e)}function Lg(t,e){e===void 0&&(e=0);var r=Ng(t),n=r.length,i,o=0;if(n>=16){for(var a=e+Dr+Mr>>>0,s=e+Mr>>>0,l=e>>>0,u=e-Dr>>>0;o<=n-16;)a=kr(a,Yt(r,o)),o+=4,s=kr(s,Yt(r,o)),o+=4,l=kr(l,Yt(r,o)),o+=4,u=kr(u,Yt(r,o)),o+=4;i=pt(a,1)+pt(s,7)+pt(l,12)+pt(u,18)>>>0}else i=e+Sa>>>0;for(i=i+n>>>0;o<=n-4;)i=i+Math.imul(Yt(r,o),Ea)>>>0,i=Math.imul(pt(i,17),xg)>>>0,o+=4;for(;o>>0,i=Math.imul(pt(i,11),Dr)>>>0,o++;return i^=i>>>15,i=Math.imul(i,Mr)>>>0,i^=i>>>13,i=Math.imul(i,Ea)>>>0,i^=i>>>16,i>>>0}function Dg(t,e){var r=Lg(t.toString()),n=r%1e6;return n/1e60?on(b,l):b;return{href:w,title:S,viewportHeight:E,viewportWidth:p,type:"url-change-event"}},g=function(){var E=v();if(d===void 0||E!==d){d=E;var p=h();e(p)}},m=Ru(r,function(){return g()},u?{enablePolling:!0,pollingInterval:c}:{});return g(),function(){return m()}},options:t}}xu();var Mg=(function(){function t(){var e=this;this.name="@amplitude/session-replay-browser",this.recordCancelCallback=null,this.eventCount=0,this.sessionTargetingMatch=!1,this.pageLeaveFns=[],this.recordFunction=null,this.urlChangeCleanup=null,this.latestUrlChangeTargetingEvaluationId=0,this.teardownEventListeners=function(r){var n=F();n&&(n.removeEventListener("blur",e.blurListener),n.removeEventListener("focus",e.focusListener),!r&&n.addEventListener("blur",e.blurListener),!r&&n.addEventListener("focus",e.focusListener),n.self&&"onpagehide"in n.self?(n.removeEventListener("pagehide",e.pageLeaveListener),!r&&n.addEventListener("pagehide",e.pageLeaveListener)):(n.removeEventListener("beforeunload",e.pageLeaveListener),!r&&n.addEventListener("beforeunload",e.pageLeaveListener)))},this.blurListener=function(){e.sendEvents()},this.focusListener=function(){e.recordEvents(!1)},this.pageLeaveListener=function(r){e.pageLeaveFns.forEach(function(n){n(r)})},this.evaluateTargetingAndCapture=function(r,n,i,o){return n===void 0&&(n=!1),i===void 0&&(i=!1),o===void 0&&(o=!1),_(e,void 0,void 0,function(){var a,s,l,u,c,f,d,v,h,g,m,E,p;return T(this,function(y){switch(y.label){case 0:if(!this.identifiers||!this.identifiers.sessionId||!this.config)return this.identifiers&&!this.identifiers.sessionId?this.loggerProvider.log("Session ID has not been set yet, cannot evaluate targeting for Session Replay."):this.loggerProvider.warn("Session replay init has not been called, cannot evaluate targeting."),[2];if(!this.config.targetingConfig)if(n)this.loggerProvider.log("Targeting config has not been set yet, cannot evaluate targeting.");else return this.loggerProvider.log("No targeting config set, skipping initialization/recording for event."),[2];return this.lastTargetingParams=r,a=this.config.targetingConfig,s=a&&!this.sessionTargetingMatch,s?(l=o?this.latestUrlChangeTargetingEvaluationId:void 0,u=r.event,u&&Object.values(xe).includes(u.event_type)&&(u=void 0),c=(E=(h=(v=r.page)===null||v===void 0?void 0:v.url)!==null&&h!==void 0?h:(m=(g=F())===null||g===void 0?void 0:g.location)===null||m===void 0?void 0:m.href)!==null&&E!==void 0?E:"",f=(p=r.page)!==null&&p!==void 0?p:c!==""?{url:c}:void 0,[4,Rg({sessionId:this.identifiers.sessionId,targetingConfig:a,loggerProvider:this.loggerProvider,apiKey:this.config.apiKey,targetingParams:{userProperties:r.userProperties,event:u,page:f},urlChange:o})]):[3,2];case 1:if(d=y.sent(),o&&l!==void 0&&l!==this.latestUrlChangeTargetingEvaluationId)return this.loggerProvider.debug("Ignoring stale URL-change targeting result #".concat(l,"; latest is #").concat(this.latestUrlChangeTargetingEvaluationId,".")),[2];this.sessionTargetingMatch=this.sessionTargetingMatch||d,this.loggerProvider.debug(JSON.stringify({name:"targeted replay capture config",sessionTargetingMatch:this.sessionTargetingMatch,event:u,targetingParams:r},null,2)),y.label=2;case 2:return n?(this.initialize(!0),[3,5]):[3,3];case 3:return i||!this.recordCancelCallback?(this.loggerProvider.log("Recording events for session due to forceRestart or no ongoing recording."),[4,this.recordEvents()]):[3,5];case 4:y.sent(),y.label=5;case 5:return[2]}})})},this.addCustomRRWebEvent=function(r,n,i){return n===void 0&&(n={}),i===void 0&&(i=!0),_(e,void 0,void 0,function(){var o,a,s,l;return T(this,function(u){switch(u.label){case 0:return u.trys.push([0,3,,4]),o=void 0,a=this.config,a&&r!==at.METADATA?(o={config:hu(a),version:ni},i?[4,vu()]:[3,2]):[3,2];case 1:s=u.sent(),o=C(C({},s),o),u.label=2;case 2:return this.recordCancelCallback&&this.recordFunction?this.recordFunction.addCustomEvent(r,C(C({},n),o)):this.loggerProvider.debug("Not able to add custom replay capture event ".concat(r," due to no ongoing recording.")),[3,4];case 3:return l=u.sent(),this.loggerProvider.debug("Error while adding custom replay capture event: ",l),[3,4];case 4:return[2]}})})},this.stopRecordingEvents=function(){var r;try{e.loggerProvider.log("Session Replay capture stopping."),e.recordCancelCallback&&e.recordCancelCallback(),e.recordCancelCallback=null,(r=e.networkObservers)===null||r===void 0||r.stop()}catch(i){var n=i;e.loggerProvider.warn("Error occurred while stopping replay capture: ".concat(n.toString()))}},this.loggerProvider=new Jn(new Dt)}return t.prototype.init=function(e,r){return Te(this._init(e,r))},t.prototype.setupUrlChangeListenerForTargeting=function(){var e=this,r;(r=this.urlChangeCleanup)===null||r===void 0||r.call(this);var n=F();if(n?.location){var i=function(a){var s=++e.latestUrlChangeTargetingEvaluationId;e.evaluateTargetingAndCapture({userProperties:{},event:void 0,page:{url:a}},!1,!1,!0),e.loggerProvider.debug("Queued URL-change targeting re-evaluation #".concat(s," for ").concat(a,"."))},o=Ru(n,i);this.urlChangeCleanup=function(){o(),e.urlChangeCleanup=null}}},t.prototype.getCurrentPageForTargeting=function(){var e,r,n=(r=(e=F())===null||e===void 0?void 0:e.location)===null||r===void 0?void 0:r.href;return n!=null?{url:n}:void 0},t.prototype._init=function(e,r){var n,i,o,a,s,l,u,c;return _(this,void 0,void 0,function(){var f,d,v,h,g,m,E,p,y,b,I,S,w,A,I,k,x,P,R;return T(this,function(O){switch(O.label){case 0:return(n=this.urlChangeCleanup)===null||n===void 0||n.call(this),this.loggerProvider=new Jn(r.loggerProvider||new Dt),Object.prototype.hasOwnProperty.call(r,"logLevel")&&this.loggerProvider.enable(r.logLevel),this.identifiers=new ba({sessionId:r.sessionId,deviceId:r.deviceId}),f=this,[4,Ph(e,r)];case 1:return f.joinedConfigGenerator=O.sent(),[4,this.joinedConfigGenerator.generateJoinedConfig()];case 2:d=O.sent(),v=d.joinedConfig,h=d.localConfig,g=d.remoteConfig,this.config=v,this.setMetadata(r.sessionId,v,h,g,(i=r.version)===null||i===void 0?void 0:i.version,ni,(o=r.version)===null||o===void 0?void 0:o.type),r.sessionId&&(!((a=this.config.interactionConfig)===null||a===void 0)&&a.enabled)&&(m=kg.default({sessionId:r.sessionId,type:"interaction"},this.config),this.pageLeaveFns=[m.send(this.getDeviceId.bind(this)).bind(m)],this.scrollHook=m.hook.bind(m),this.clickHandler=new Ag(this.loggerProvider,m)),E=[],p=this.config.storeType,p==="idb"&&!(!((s=F())===null||s===void 0)&&s.indexedDB)&&(p="memory",this.loggerProvider.warn("Could not use preferred indexedDB storage, reverting to in memory option.")),this.loggerProvider.log("Using ".concat(p," for event storage.")),O.label=3;case 3:return O.trys.push([3,5,,6]),[4,ha({config:this.config,type:"replay",storeType:p})];case 4:return y=O.sent(),E.push({name:"replay",manager:y}),[3,6];case 5:return b=O.sent(),I=b,this.loggerProvider.warn("Error occurred while creating replay events manager: ".concat(I.toString())),[3,6];case 6:if(!(!((l=this.config.interactionConfig)===null||l===void 0)&&l.enabled))return[3,10];S=this.config.interactionConfig.batch?Ig:_g,O.label=7;case 7:return O.trys.push([7,9,,10]),[4,ha({config:this.config,type:"interaction",minInterval:(u=this.config.interactionConfig.trackEveryNms)!==null&&u!==void 0?u:vh,maxInterval:hh,payloadBatcher:S,storeType:p})];case 8:return w=O.sent(),E.push({name:"interaction",manager:w}),[3,10];case 9:return A=O.sent(),I=A,this.loggerProvider.warn("Error occurred while creating interaction events manager: ".concat(I.toString())),[3,10];case 10:return this.eventsManager=new(ga.bind.apply(ga,$([void 0],D(E),!1))),this.eventCompressor&&this.eventCompressor.terminate(),k=void 0,x=F(),this.config.useWebWorker&&x&&x.Worker?[4,Qt(()=>import("./D7Bf6Say.js"),[])]:[3,12];case 11:P=O.sent().compressionScript,k=P,O.label=12;case 12:return this.eventCompressor=new Kh(this.eventsManager,this.config,this.getDeviceId(),k),[4,this.initializeNetworkObservers()];case 13:return O.sent(),!((c=F())===null||c===void 0)&&c.opener&&(R=ys(),bs(R),R.setup(C({logger:this.loggerProvider},this.config.serverZone&&{endpoint:ps[this.config.serverZone]}))),this.loggerProvider.log("Installing @amplitude/session-replay-browser."),this.teardownEventListeners(!1),[4,this.evaluateTargetingAndCapture({userProperties:r.userProperties,page:this.getCurrentPageForTargeting()},!0)];case 14:return O.sent(),this.config.targetingConfig&&this.setupUrlChangeListenerForTargeting(),[2]}})})},t.prototype.setSessionId=function(e,r){return Te(this.asyncSetSessionId(e,r))},t.prototype.asyncSetSessionId=function(e,r,n){var i;return _(this,void 0,void 0,function(){var o,a,s;return T(this,function(l){switch(l.label){case 0:return this.latestUrlChangeTargetingEvaluationId++,this.sessionTargetingMatch=!1,this.lastShouldRecordDecision=void 0,o=this.identifiers&&this.identifiers.sessionId,o&&this.sendEvents(o),a=r||this.getDeviceId(),this.identifiers=new ba({sessionId:e,deviceId:a}),this.joinedConfigGenerator&&o?[4,this.joinedConfigGenerator.generateJoinedConfig()]:[3,2];case 1:s=l.sent().joinedConfig,this.config=s,l.label=2;case 2:return!((i=this.config)===null||i===void 0)&&i.targetingConfig?[4,this.evaluateTargetingAndCapture({userProperties:n?.userProperties,page:this.getCurrentPageForTargeting()},!1,!0)]:[3,4];case 3:return l.sent(),[3,6];case 4:return[4,this.recordEvents()];case 5:l.sent(),l.label=6;case 6:return[2]}})})},t.prototype.getSessionReplayProperties=function(){var e,r=this.config,n=this.identifiers;if(!r||!n)return this.loggerProvider.warn("Session replay init has not been called, cannot get session replay properties."),{};var i=this.getShouldRecord(),o={};return i&&(o=(e={},e[oh]=n.sessionReplayId?n.sessionReplayId:null,e),r.debugMode&&(o[lh]=JSON.stringify({appHash:Vn(r.apiKey).toString()}))),this.addCustomRRWebEvent(at.GET_SR_PROPS,{shouldRecord:i,eventProperties:o},this.eventCount===10),this.eventCount===10&&(this.eventCount=0),this.eventCount++,o},t.prototype.sendEvents=function(e){var r,n=e||((r=this.identifiers)===null||r===void 0?void 0:r.sessionId),i=this.getDeviceId();this.eventsManager&&n&&i&&this.eventsManager.sendCurrentSequenceEvents({sessionId:n,deviceId:i})},t.prototype.initialize=function(e){var r;return e===void 0&&(e=!1),_(this,void 0,void 0,function(){var n;return T(this,function(i){return!((r=this.identifiers)===null||r===void 0)&&r.sessionId?(n=this.getDeviceId(),n?(this.eventsManager&&e&&this.eventsManager.sendStoredEvents({deviceId:n}),[2,this.recordEvents()]):(this.loggerProvider.log("Session is not being recorded due to lack of device id."),[2,Promise.resolve()])):(this.loggerProvider.log("Session is not being recorded due to lack of session id."),[2,Promise.resolve()])})})},t.prototype.shouldOptOut=function(){var e,r,n;if(!((e=this.config)===null||e===void 0)&&e.instanceName){var i=Mt(this.config.instanceName).identityStore;n=i.getIdentity().optOut}return n!==void 0?n:(r=this.config)===null||r===void 0?void 0:r.optOut},t.prototype.getShouldRecord=function(){if(!this.identifiers||!this.config||!this.identifiers.sessionId)return this.loggerProvider.warn("Session is not being recorded due to lack of config, please call sessionReplay.init."),!1;if(!this.config.captureEnabled)return this.loggerProvider.log("Session ".concat(this.identifiers.sessionId," not being captured due to capture being disabled for project or because the remote config could not be fetched.")),!1;if(this.shouldOptOut())return this.loggerProvider.log("Opting session ".concat(this.identifiers.sessionId," out of recording due to optOut config.")),!1;var e=!1,r="",n=!1;if(this.config.targetingConfig)this.sessionTargetingMatch?(r="Capturing replays for session ".concat(this.identifiers.sessionId," due to matching targeting conditions."),this.loggerProvider.log(r),e=!0,n=!0):(r="Not capturing replays for session ".concat(this.identifiers.sessionId," due to not matching targeting conditions."),this.loggerProvider.log(r),e=!1,n=!1);else{var i=Dg(this.identifiers.sessionId,this.config.sampleRate);i?(e=!0,n=!0):(r="Opting session ".concat(this.identifiers.sessionId," out of recording due to sample rate."),this.loggerProvider.log(r),e=!1,n=!1)}return this.lastShouldRecordDecision!==e&&this.config.targetingConfig&&(this.addCustomRRWebEvent(at.TARGETING_DECISION,{message:r,sessionId:this.identifiers.sessionId,matched:n,targetingParams:this.lastTargetingParams}),this.lastShouldRecordDecision=e),e},t.prototype.getBlockSelectors=function(){var e,r,n,i=(n=(r=(e=this.config)===null||e===void 0?void 0:e.privacyConfig)===null||r===void 0?void 0:r.blockSelector)!==null&&n!==void 0?n:[];if(i.length!==0)return i},t.prototype.getMaskTextSelectors=function(){var e,r,n,i;if(((r=(e=this.config)===null||e===void 0?void 0:e.privacyConfig)===null||r===void 0?void 0:r.defaultMaskLevel)==="conservative")return"*";var o=(i=(n=this.config)===null||n===void 0?void 0:n.privacyConfig)===null||i===void 0?void 0:i.maskSelector;if(o)return o},t.prototype.getRecordingPlugins=function(e){var r,n,i,o,a,s;return _(this,void 0,void 0,function(){var l,u,c,f;return T(this,function(d){switch(d.label){case 0:l=[];try{u=xu({ugcFilterRules:((n=(r=this.config)===null||r===void 0?void 0:r.interactionConfig)===null||n===void 0?void 0:n.ugcFilterRules)||[],enablePolling:((i=this.config)===null||i===void 0?void 0:i.enableUrlChangePolling)||!1,pollingInterval:(o=this.config)===null||o===void 0?void 0:o.urlChangePollingInterval,captureDocumentTitle:(a=this.config)===null||a===void 0?void 0:a.captureDocumentTitle}),l.push(u)}catch(v){this.loggerProvider.warn("Failed to create URL tracking plugin:",v)}if(!(!((s=e?.console)===null||s===void 0)&&s.enabled))return[3,4];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,Qt(()=>import("./sPdhR0i8.js"),[])];case 2:return c=d.sent().getRecordConsolePlugin,l.push(c({level:e.console.levels})),[3,4];case 3:return f=d.sent(),this.loggerProvider.warn("Failed to load console plugin:",f),[3,4];case 4:return[2,l.length>0?l:void 0]}})})},t.prototype.getRecordFunction=function(){return _(this,void 0,void 0,function(){var e,r;return T(this,function(n){switch(n.label){case 0:if(this.recordFunction)return[2,this.recordFunction];n.label=1;case 1:return n.trys.push([1,3,,4]),[4,Qt(()=>import("./DMrp4Fbu.js"),[])];case 2:return e=n.sent().record,this.recordFunction=e,[2,e];case 3:return r=n.sent(),this.loggerProvider.warn("Failed to load rrweb-record module:",r),[2,null];case 4:return[2]}})})},t.prototype.recordEvents=function(e){var r,n,i,o,a,s,l,u;return e===void 0&&(e=!0),_(this,void 0,void 0,function(){var c,f,d,v,h,g,m,E,p,y,b,S,w,A,I,k,x=this;return T(this,function(P){switch(P.label){case 0:return c=this.config,f=this.getShouldRecord(),d=(r=this.identifiers)===null||r===void 0?void 0:r.sessionId,!f||!d||!c?[2]:(this.stopRecordingEvents(),[4,this.getRecordFunction()]);case 1:return v=P.sent(),v?[4,this.initializeNetworkObservers()]:[2];case 2:P.sent(),h=(n=c.loggingConfig)===null||n===void 0?void 0:n.network,g=Oi(c.serverZone,c.trackServerUrl),m=[su,uu,lu,g],(i=this.networkObservers)===null||i===void 0||i.start(function(R){m.some(function(O){return R.url.startsWith(O)})||x.addCustomRRWebEvent(at.FETCH_REQUEST,R)},h),E=c.privacyConfig,p=c.interactionConfig,y=c.loggingConfig,b=p?.enabled?{mouseInteraction:this.eventsManager&&((o=this.clickHandler)===null||o===void 0?void 0:o.createHook({eventsManager:this.eventsManager,sessionId:d,deviceIdFn:this.getDeviceId.bind(this),mirror:v.mirror,ugcFilterRules:(a=p.ugcFilterRules)!==null&&a!==void 0?a:[],performanceOptions:(s=c.performanceConfig)===null||s===void 0?void 0:s.interaction})),scroll:this.scrollHook}:{},S=p?.enabled&&p.ugcFilterRules?p.ugcFilterRules:[],this.loggerProvider.log("Session Replay capture beginning for ".concat(d,".")),P.label=3;case 3:return P.trys.push([3,5,,6]),w=this,A=v,k={emit:function(R){if(x.shouldOptOut()){x.loggerProvider.log("Opting session ".concat(d," out of recording due to optOut config.")),x.stopRecordingEvents(),x.sendEvents();return}R.type===pu.Meta&&(R.data.href=on(R.data.href,S)),x.eventCompressor&&x.eventCompressor.enqueueEvent(R,d)},inlineStylesheet:c.shouldInlineStylesheet,hooks:b,maskAllInputs:!0,maskTextClass:au,blockClass:ch,blockSelector:this.getBlockSelectors(),applyBackgroundColorToBlockedElements:c.applyBackgroundColorToBlockedElements,maskInputFn:na("input",E),maskTextFn:na("text",E),maskAttributeFn:Eh(E),maskTextSelector:this.getMaskTextSelectors(),recordCanvas:!1,slimDOMOptions:{script:(l=c.omitElementTags)===null||l===void 0?void 0:l.script,comment:(u=c.omitElementTags)===null||u===void 0?void 0:u.comment},errorHandler:function(R){var O=R;if(O.message.includes("insertRule")&&O.message.includes("CSSStyleSheet")||O._external_)throw O;return x.loggerProvider.warn("Error while capturing replay: ",O.toString()),!0}},[4,this.getRecordingPlugins(y)];case 4:return w.recordCancelCallback=A.apply(void 0,[(k.plugins=P.sent(),k)]),this.addCustomRRWebEvent(at.DEBUG_INFO),e&&this.addCustomRRWebEvent(at.METADATA,this.metadata),[3,6];case 5:return I=P.sent(),this.loggerProvider.warn("Failed to initialize session replay:",I),[3,6];case 6:return[2]}})})},t.prototype.getDeviceId=function(){var e;return(e=this.identifiers)===null||e===void 0?void 0:e.deviceId},t.prototype.getSessionId=function(){var e;return(e=this.identifiers)===null||e===void 0?void 0:e.sessionId},t.prototype.flush=function(e){var r;return e===void 0&&(e=!1),_(this,void 0,void 0,function(){return T(this,function(n){return[2,(r=this.eventsManager)===null||r===void 0?void 0:r.flush(e)]})})},t.prototype.shutdown=function(){var e;(e=this.urlChangeCleanup)===null||e===void 0||e.call(this),this.teardownEventListeners(!0),this.stopRecordingEvents(),this.sendEvents()},t.prototype.mapSDKType=function(e){return e==="plugin"?"@amplitude/plugin-session-replay-browser":e==="segment"?"@amplitude/segment-session-replay-plugin":null},t.prototype.setMetadata=function(e,r,n,i,o,a,s){var l=e?.toString()?Vn(e.toString()):void 0;this.metadata={joinedConfig:r,localConfig:n,remoteConfig:i,sessionId:e,hashValue:l,sampleRate:r.sampleRate,replaySDKType:this.mapSDKType(s),replaySDKVersion:o,standaloneSDKType:"@amplitude/session-replay-browser",standaloneSDKVersion:a}},t.prototype.initializeNetworkObservers=function(){var e,r,n;return _(this,void 0,void 0,function(){var i,o;return T(this,function(a){switch(a.label){case 0:if(!(!((n=(r=(e=this.config)===null||e===void 0?void 0:e.loggingConfig)===null||r===void 0?void 0:r.network)===null||n===void 0)&&n.enabled&&!this.networkObservers))return[3,4];a.label=1;case 1:return a.trys.push([1,3,,4]),[4,Qt(()=>import("./CifMVRjc.js"),[])];case 2:return i=a.sent().NetworkObservers,this.networkObservers=new i,[3,4];case 3:return o=a.sent(),this.loggerProvider.warn("Failed to import or instantiate NetworkObservers:",o),[3,4];case 4:return[2]}})})},t})(),gt=function(t){return function(){var e=t.config,r=e||gu(),n=r.loggerProvider,i=r.logLevel;return{logger:n,logLevel:i}}},Ug=function(){var t=new Mg;return{init:Q(t.init.bind(t),"init",gt(t)),evaluateTargetingAndCapture:Q(t.evaluateTargetingAndCapture.bind(t),"evaluateTargetingAndRecord",gt(t)),setSessionId:Q(t.setSessionId.bind(t),"setSessionId",gt(t)),getSessionId:Q(t.getSessionId.bind(t),"getSessionId",gt(t)),getSessionReplayProperties:Q(t.getSessionReplayProperties.bind(t),"getSessionReplayProperties",gt(t)),flush:Q(t.flush.bind(t),"flush",gt(t)),shutdown:Q(t.shutdown.bind(t),"shutdown",gt(t))}};const wt=Ug();var Fg=wt.init,qg=wt.setSessionId,Bg=wt.getSessionId,Vg=wt.getSessionReplayProperties,jg=wt.flush,Hg=wt.shutdown,Gg=wt.evaluateTargetingAndCapture,Wg=function(t){return t===void 0&&(t="$default_instance"),mi.getInstance(t)},st;(function(t){t.SET="$set",t.SET_ONCE="$setOnce",t.ADD="$add",t.APPEND="$append",t.PREPEND="$prepend",t.REMOVE="$remove",t.PREINSERT="$preInsert",t.POSTINSERT="$postInsert",t.UNSET="$unset",t.CLEAR_ALL="$clearAll"})(st||(st={}));var Ta;(function(t){t.REVENUE_PRODUCT_ID="$productId",t.REVENUE_QUANTITY="$quantity",t.REVENUE_PRICE="$price",t.REVENUE_TYPE="$revenueType",t.REVENUE_CURRENCY="$currency",t.REVENUE="$revenue"})(Ta||(Ta={}));var _a;(function(t){t.IDENTIFY="$identify",t.GROUP_IDENTIFY="$groupidentify",t.REVENUE="revenue_amount"})(_a||(_a={}));var Kg=[st.SET,st.SET_ONCE,st.ADD,st.APPEND,st.PREPEND,st.POSTINSERT],zg=function(t){if(t.user_properties){var e={},r=Object.keys(t.user_properties);return r.forEach(function(n){if(Kg.includes(n)){var i=t.user_properties&&t.user_properties[n];e=C(C({},e),i)}}),e}},Ia="1.27.6",$g="[Amplitude]",Xg="".concat($g," Session Replay ID"),li=(function(){function t(e){this.name=t.pluginName,this.type="enrichment",this.config=null,this.sessionReplay={flush:jg,getSessionId:Bg,getSessionReplayProperties:Vg,init:Fg,setSessionId:qg,shutdown:Hg,evaluateTargetingAndCapture:Gg},this.options=C({forceSessionTracking:!1},e),this.srInitOptions=this.options}return t.prototype.setup=function(e,r){var n,i,o,a,s,l,u,c;return _(this,void 0,void 0,function(){var f,d,v;return T(this,function(h){switch(h.label){case 0:return h.trys.push([0,2,,3]),e?.loggerProvider.log("Installing @amplitude/plugin-session-replay, version ".concat(Ia,".")),this.config=e,this.options.forceSessionTracking&&(typeof e.defaultTracking=="boolean"?e.defaultTracking===!1&&(e.defaultTracking={pageViews:!1,formInteractions:!1,fileDownloads:!1,sessions:!0}):e.defaultTracking=C(C({},e.defaultTracking),{sessions:!0})),f=Wg(this.config.instanceName).identityStore,d=f.getIdentity().userProperties,this.srInitOptions={instanceName:this.config.instanceName,deviceId:(n=this.options.deviceId)!==null&&n!==void 0?n:this.config.deviceId,optOut:this.config.optOut,sessionId:this.options.customSessionId?void 0:this.config.sessionId,loggerProvider:this.config.loggerProvider,logLevel:this.config.logLevel,flushMaxRetries:this.config.flushMaxRetries,serverZone:this.config.serverZone,configServerUrl:this.options.configServerUrl||((i=this.config.remoteConfig)===null||i===void 0?void 0:i.serverUrl),trackServerUrl:this.options.trackServerUrl,sampleRate:this.options.sampleRate,privacyConfig:{blockSelector:(o=this.options.privacyConfig)===null||o===void 0?void 0:o.blockSelector,maskSelector:(a=this.options.privacyConfig)===null||a===void 0?void 0:a.maskSelector,unmaskSelector:(s=this.options.privacyConfig)===null||s===void 0?void 0:s.unmaskSelector,defaultMaskLevel:(l=this.options.privacyConfig)===null||l===void 0?void 0:l.defaultMaskLevel},debugMode:this.options.debugMode,shouldInlineStylesheet:this.options.shouldInlineStylesheet,version:{type:"plugin",version:Ia},performanceConfig:this.options.performanceConfig,storeType:this.options.storeType,useWebWorker:(u=this.options.useWebWorker)!==null&&u!==void 0?u:(c=this.options.experimental)===null||c===void 0?void 0:c.useWebWorker,userProperties:d,omitElementTags:this.options.omitElementTags,applyBackgroundColorToBlockedElements:this.options.applyBackgroundColorToBlockedElements,interactionConfig:this.options.interactionConfig,captureDocumentTitle:this.options.captureDocumentTitle,enableUrlChangePolling:this.options.enableUrlChangePolling,urlChangePollingInterval:this.options.urlChangePollingInterval},[4,this.sessionReplay.init(e.apiKey,this.srInitOptions).promise];case 1:return h.sent(),[3,3];case 2:return v=h.sent(),e?.loggerProvider.error("Session Replay: Failed to initialize due to ".concat(v.message)),[3,3];case 3:return[2]}})})},t.prototype.onSessionIdChanged=function(e){var r;return _(this,void 0,void 0,function(){return T(this,function(n){switch(n.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("Analytics session id is changed to ".concat(e,", SR session id is ").concat(String(this.sessionReplay.getSessionId()),".")),[4,this.sessionReplay.setSessionId(e).promise];case 1:return n.sent(),[2]}})})},t.prototype.onOptOutChanged=function(e){var r;return _(this,void 0,void 0,function(){var n;return T(this,function(i){switch(i.label){case 0:return(r=this.config)===null||r===void 0||r.loggerProvider.debug("optOut is changed to ".concat(String(e),", calling ").concat(e?"sessionReplay.shutdown()":"sessionReplay.init()",".")),e?(this.sessionReplay.shutdown(),[3,4]):[3,1];case 1:return n=this.config!=null,n?[4,this.sessionReplay.init(this.config.apiKey,this.srInitOptions).promise]:[3,3];case 2:n=i.sent(),i.label=3;case 3:i.label=4;case 4:return[2]}})})},t.prototype.execute=function(e){var r,n,i;return _(this,void 0,void 0,function(){var o,l,o,a,s,l,u;return T(this,function(c){switch(c.label){case 0:return c.trys.push([0,9,,10]),this.options.customSessionId?(o=this.options.customSessionId(e),o?o===this.sessionReplay.getSessionId()?[3,2]:[4,this.sessionReplay.setSessionId(o).promise]:[3,3]):[3,4];case 1:c.sent(),c.label=2;case 2:l=this.sessionReplay.getSessionReplayProperties(),e.event_properties=C(C({},e.event_properties),l),c.label=3;case 3:return[3,8];case 4:return o=(r=this.config)===null||r===void 0?void 0:r.sessionId,o&&o!==this.sessionReplay.getSessionId()?[4,this.sessionReplay.setSessionId(o).promise]:[3,6];case 5:c.sent(),c.label=6;case 6:return o&&o===e.session_id?(a=void 0,e.event_type===xe.IDENTIFY&&(a=zg(e)),s=F(),[4,this.sessionReplay.evaluateTargetingAndCapture({event:e,userProperties:a,page:((n=s?.location)===null||n===void 0?void 0:n.href)!=null?{url:s.location.href}:void 0})]):[3,8];case 7:c.sent(),l=this.sessionReplay.getSessionReplayProperties(),e.event_properties=C(C({},e.event_properties),l),c.label=8;case 8:return e.event_type===xe.IDENTIFY&&e.event_properties&&delete e.event_properties[Xg],[2,Promise.resolve(e)];case 9:return u=c.sent(),(i=this.config)===null||i===void 0||i.loggerProvider.error("Session Replay: Failed to enrich event due to ".concat(u.message)),[2,Promise.resolve(e)];case 10:return[2]}})})},t.prototype.teardown=function(){var e;return _(this,void 0,void 0,function(){return T(this,function(r){try{this.sessionReplay.shutdown(),this.config=null}catch(n){(e=this.config)===null||e===void 0||e.loggerProvider.error("Session Replay: teardown failed due to ".concat(n.message))}return[2]})})},t.prototype.getSessionReplayProperties=function(){return this.sessionReplay.getSessionReplayProperties()},t.pluginName="@amplitude/plugin-session-replay-browser",t})(),Yg=function(t){return new li(t)},ci=function(t,e){return ci=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(r,n){r.__proto__=n}||function(r,n){for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(r[i]=n[i])},ci(t,e)};function Nu(t,e){if(typeof e!="function"&&e!==null)throw new TypeError("Class extends value "+String(e)+" is not a constructor or null");ci(t,e);function r(){this.constructor=t}t.prototype=e===null?Object.create(e):(r.prototype=e.prototype,new r)}var lt=function(){return lt=Object.assign||function(e){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(u[0]===6||u[0]===2)){r=0;continue}if(u[0]===3&&(!o||u[1]>o[0]&&u[1]=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function Li(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),i,o=[],a;try{for(;(e===void 0||e-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(s){a={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return o}function Di(t,e,r){if(arguments.length===2)for(var n=0,i=e.length,o;n>6|192,e[r++]=i&63|128):(i&64512)==55296&&n+1>18|240,e[r++]=i>>12&63|128,e[r++]=i>>6&63|128,e[r++]=i&63|128):(e[r++]=i>>12|224,e[r++]=i>>6&63|128,e[r++]=i&63|128)}return Uint8Array.from(e)},Ur=-862048943,Fr=461845907,qr=15,Qg=13,Zg=5,ep=-430675100,tp=function(t,e){e===void 0&&(e=0);for(var r=Jg(t),n=r.length,i=n>>2,o=e,a=0;a>>0},rp=function(t,e){var r=t,n=e;return r=Math.imul(r,Ur),r=ur(r,qr),r=Math.imul(r,Fr),n^=r,n=ur(n,Qg),n=Math.imul(n,Zg),n+ep|0},np=function(t){var e=t;return e^=e>>>16,e=Math.imul(e,-2048144789),e^=e>>>13,e=Math.imul(e,-1028477387),e^=e>>>16,e},ur=function(t,e,r){r===void 0&&(r=32),e>r&&(e=e%r);var n=4294967295<>>0,i=(t&n)>>>0>>>r-e>>>0;return(t<>>0},ip=function(t,e){e===void 0&&(e=0);var r=t[e]<<24|t[e+1]<<16|t[e+2]<<8|t[e+3];return op(r)},op=function(t){return(t&-16777216)>>>24|(t&16711680)>>>8|(t&65280)<<8|(t&255)<<24},Aa=function(t,e){var r,n;if(!(!e||e.length===0)){try{for(var i=Le(e),o=i.next();!o.done;o=i.next()){var a=o.value;if(!a||!t||typeof t!="object")return;t=t[a]}}catch(s){r={error:s}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}if(t!=null)return t}},ap="(\\d+)\\.(\\d+)",sp="(\\d+)",up="(-(([-\\w]+\\.?)*))?",lp="^".concat(ap,"(\\.").concat(sp).concat(up,")?$"),cp=(function(){function t(e,r,n,i){i===void 0&&(i=void 0),this.major=e,this.minor=r,this.patch=n,this.preRelease=i}return t.parse=function(e){if(e){var r=new RegExp(lp).exec(e);if(r){var n=Number(r[1]),i=Number(r[2]);if(!(isNaN(n)||isNaN(i))){var o=Number(r[4])||0,a=r[5]||void 0;return new t(n,i,o,a)}}}},t.prototype.compareTo=function(e){return this.major>e.major?1:this.majore.minor?1:this.minore.patch?1:this.patche.preRelease?1:this.preRelease=g&&c=b&&fn;case U.GREATER_THAN_EQUALS:case U.VERSION_GREATER_THAN_EQUALS:return e>=n;default:return!1}},t.prototype.versionComparator=function(e,r,n){var i=e.compareTo(n);switch(r){case U.LESS_THAN:case U.VERSION_LESS_THAN:return i<0;case U.LESS_THAN_EQUALS:case U.VERSION_LESS_THAN_EQUALS:return i<=0;case U.GREATER_THAN:case U.VERSION_GREATER_THAN:return i>0;case U.GREATER_THAN_EQUALS:case U.VERSION_GREATER_THAN_EQUALS:return i>=0;default:return!1}},t.prototype.matchesRegex=function(e,r){return r.some(function(n){return!!new RegExp(n).exec(e)})},t.prototype.containsNone=function(e){return e.some(function(r){return r==="(none)"})},t.prototype.containsBooleans=function(e){return e.some(function(r){switch(r.toLowerCase()){case"true":case"false":return!0;default:return!1}})},t.prototype.parseNumber=function(e){var r;return(r=Number(e))!==null&&r!==void 0?r:void 0},t.prototype.coerceString=function(e){if(e!=null)return typeof e=="object"?JSON.stringify(e):String(e)},t.prototype.coerceStringArray=function(e){var r=this;if(Array.isArray(e)){var n=e;return n.map(function(s){return r.coerceString(s)}).filter(Boolean)}var i=String(e);try{var o=JSON.parse(i);if(Array.isArray(o)){var n=e;return n.map(function(l){return r.coerceString(l)}).filter(Boolean)}else{var a=this.coerceString(i);return a?[a]:void 0}}catch{var a=this.coerceString(i);return a?[a]:void 0}},t.prototype.isSetOperator=function(e){switch(e){case U.SET_IS:case U.SET_IS_NOT:case U.SET_CONTAINS:case U.SET_DOES_NOT_CONTAIN:case U.SET_CONTAINS_ANY:case U.SET_DOES_NOT_CONTAIN_ANY:return!0;default:return!1}},t.prototype.setEquals=function(e,r){var n=new Set(e),i=new Set(r);return n.size===i.size&&Di([],Li(i),!1).every(function(o){return n.has(o)})},t.prototype.matchesSetContainsAll=function(e,r){var n,i;if(e.length{let e={};return t.forEach((r,n)=>e[r]=n),e})(Zt),gp=/^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/,_e=String.fromCharCode.bind(String),Pa=typeof Uint8Array.from=="function"?Uint8Array.from.bind(Uint8Array):t=>new Uint8Array(Array.prototype.slice.call(t,0)),Fu=t=>t.replace(/=/g,"").replace(/[+\/]/g,e=>e=="+"?"-":"_"),qu=t=>t.replace(/[^A-Za-z0-9\+\/]/g,""),Bu=t=>{let e,r,n,i,o="";const a=t.length%3;for(let s=0;s255||(n=t.charCodeAt(s++))>255||(i=t.charCodeAt(s++))>255)throw new TypeError("invalid character found");e=r<<16|n<<8|i,o+=Zt[e>>18&63]+Zt[e>>12&63]+Zt[e>>6&63]+Zt[e&63]}return a?o.slice(0,a-3)+"===".substring(a):o},Mi=typeof btoa=="function"?t=>btoa(t):Bt?t=>Buffer.from(t,"binary").toString("base64"):Bu,di=Bt?t=>Buffer.from(t).toString("base64"):t=>{let r=[];for(let n=0,i=t.length;ne?Fu(di(t)):di(t),pp=t=>{if(t.length<2){var e=t.charCodeAt(0);return e<128?t:e<2048?_e(192|e>>>6)+_e(128|e&63):_e(224|e>>>12&15)+_e(128|e>>>6&63)+_e(128|e&63)}else{var e=65536+(t.charCodeAt(0)-55296)*1024+(t.charCodeAt(1)-56320);return _e(240|e>>>18&7)+_e(128|e>>>12&63)+_e(128|e>>>6&63)+_e(128|e&63)}},mp=/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g,Vu=t=>t.replace(mp,pp),Oa=Bt?t=>Buffer.from(t,"utf8").toString("base64"):ka?t=>di(ka.encode(t)):t=>Mi(Vu(t)),Lt=(t,e=!1)=>e?Fu(Oa(t)):Oa(t),Ra=t=>Lt(t,!0),yp=/[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}/g,bp=t=>{switch(t.length){case 4:var e=(7&t.charCodeAt(0))<<18|(63&t.charCodeAt(1))<<12|(63&t.charCodeAt(2))<<6|63&t.charCodeAt(3),r=e-65536;return _e((r>>>10)+55296)+_e((r&1023)+56320);case 3:return _e((15&t.charCodeAt(0))<<12|(63&t.charCodeAt(1))<<6|63&t.charCodeAt(2));default:return _e((31&t.charCodeAt(0))<<6|63&t.charCodeAt(1))}},ju=t=>t.replace(yp,bp),Hu=t=>{if(t=t.replace(/\s+/g,""),!gp.test(t))throw new TypeError("malformed base64.");t+="==".slice(2-(t.length&3));let e,r="",n,i;for(let o=0;o>16&255):i===64?_e(e>>16&255,e>>8&255):_e(e>>16&255,e>>8&255,e&255);return r},Ui=typeof atob=="function"?t=>atob(qu(t)):Bt?t=>Buffer.from(t,"base64").toString("binary"):Hu,Gu=Bt?t=>Pa(Buffer.from(t,"base64")):t=>Pa(Ui(t).split("").map(e=>e.charCodeAt(0))),Wu=t=>Gu(Ku(t)),Ep=Bt?t=>Buffer.from(t,"base64").toString("utf8"):Ca?t=>Ca.decode(Gu(t)):t=>ju(Ui(t)),Ku=t=>qu(t.replace(/[-_]/g,e=>e=="-"?"+":"/")),fi=t=>Ep(Ku(t)),Sp=t=>{if(typeof t!="string")return!1;const e=t.replace(/\s+/g,"").replace(/={0,2}$/,"");return!/[^\s0-9a-zA-Z\+/]/.test(e)||!/[^\s0-9a-zA-Z\-_]/.test(e)},zu=t=>({value:t,enumerable:!1,writable:!0,configurable:!0}),$u=function(){const t=(e,r)=>Object.defineProperty(String.prototype,e,zu(r));t("fromBase64",function(){return fi(this)}),t("toBase64",function(e){return Lt(this,e)}),t("toBase64URI",function(){return Lt(this,!0)}),t("toBase64URL",function(){return Lt(this,!0)}),t("toUint8Array",function(){return Wu(this)})},Xu=function(){const t=(e,r)=>Object.defineProperty(Uint8Array.prototype,e,zu(r));t("toBase64",function(e){return Br(this,e)}),t("toBase64URI",function(){return Br(this,!0)}),t("toBase64URL",function(){return Br(this,!0)})},wp=()=>{$u(),Xu()},vi={version:Uu,VERSION:vp,atob:Ui,atobPolyfill:Hu,btoa:Mi,btoaPolyfill:Bu,fromBase64:fi,toBase64:Lt,encode:Lt,encodeURI:Ra,encodeURL:Ra,utob:Vu,btou:ju,decode:fi,isValid:Sp,fromUint8Array:Br,toUint8Array:Wu,extendString:$u,extendUint8Array:Xu,extendBuiltins:wp};var Yu=(function(t){Nu(e,t);function e(r,n){var i=t.call(this,n)||this;return i.statusCode=r,Object.setPrototypeOf(i,e.prototype),i}return e})(Error),hi=(function(t){Nu(e,t);function e(r){var n=t.call(this,r)||this;return Object.setPrototypeOf(n,e.prototype),n}return e})(Error),Tp=(function(){function t(e,r,n){this.deploymentKey=e,this.serverUrl=r,this.httpClient=n}return t.prototype.getVariants=function(e,r){return Lu(this,void 0,void 0,function(){var n,i,o,a;return Du(this,function(s){switch(s.label){case 0:return n=vi.encodeURL(JSON.stringify(e)),i={Authorization:"Api-Key ".concat(this.deploymentKey),"X-Amp-Exp-User":n},r?.flagKeys&&(i["X-Amp-Exp-Flag-Keys"]=vi.encodeURL(JSON.stringify(r.flagKeys))),r?.trackingOption&&(i["X-Amp-Exp-Track"]=r.trackingOption),r?.exposureTrackingOption&&(i["X-Amp-Exp-Exposure-Track"]=r.exposureTrackingOption),o=new URL("".concat(this.serverUrl,"/sdk/v2/vardata?v=0")),r?.evaluationMode&&o.searchParams.append("eval_mode",r?.evaluationMode),r?.deliveryMethod&&o.searchParams.append("delivery_method",r?.deliveryMethod),[4,this.httpClient.request({requestUrl:o.toString(),method:"GET",headers:i,timeoutMillis:r?.timeoutMillis})];case 1:if(a=s.sent(),a.status!=200)throw new Yu(a.status,"Fetch error response: status=".concat(a.status));return[2,JSON.parse(a.body)]}})})},t})(),_p=(function(){function t(e,r,n){this.deploymentKey=e,this.serverUrl=r,this.httpClient=n}return t.prototype.getFlags=function(e){return Lu(this,void 0,void 0,function(){var r,n,i;return Du(this,function(o){switch(o.label){case 0:return r={Authorization:"Api-Key ".concat(this.deploymentKey)},e?.libraryName&&e?.libraryVersion&&(r["X-Amp-Exp-Library"]="".concat(e.libraryName,"/").concat(e.libraryVersion)),e?.user&&(r["X-Amp-Exp-User"]=vi.encodeURL(JSON.stringify(e.user))),[4,this.httpClient.request({requestUrl:"".concat(this.serverUrl,"/sdk/v2/flags")+(e?.deliveryMethod?"?delivery_method=".concat(e.deliveryMethod):""),method:"GET",headers:r,timeoutMillis:e?.timeoutMillis})];case 1:if(n=o.sent(),n.status!=200)throw Error("Flags error response: status=".concat(n.status));return i=JSON.parse(n.body),[2,i.reduce(function(a,s){return a[s.key]=s,a},{})]}})})},t})(),ye=typeof globalThis<"u"?globalThis:global||self,Tt=function(){if(typeof globalThis<"u")return globalThis;if(typeof window<"u")return window;if(typeof self<"u")return self;if(typeof global<"u")return global},Ip=function(){var t=Tt();if(t)try{var e="EXP_test";return t.localStorage.setItem(e,e),t.localStorage.removeItem(e),!0}catch{return!1}return!1},Ap=(function(){function t(e,r){this.poller=void 0,this.action=e,this.ms=r}return t.prototype.start=function(){this.poller||(this.poller=ye.setInterval(this.action,this.ms),this.action())},t.prototype.stop=function(){this.poller&&(ye.clearInterval(this.poller),this.poller=void 0)},t})(),er={exports:{}},Cp=er.exports,xa;function kp(){return xa||(xa=1,(function(t,e){(function(r,n){var i="0.7.33",o="",a="?",s="function",l="undefined",u="object",c="string",f="major",d="model",v="name",h="type",g="vendor",m="version",E="architecture",p="console",y="mobile",b="tablet",S="smarttv",w="wearable",A="embedded",I=350,k="Amazon",x="Apple",P="ASUS",R="BlackBerry",O="Browser",L="Chrome",B="Edge",H="Firefox",j="Google",ee="Huawei",M="LG",G="Microsoft",W="Motorola",te="Opera",J="Samsung",Z="Sharp",K="Sony",ie="Xiaomi",oe="Zebra",fe="Facebook",ve=function(V,z){var X={};for(var ce in V)z[ce]&&z[ce].length%2===0?X[ce]=z[ce].concat(V[ce]):X[ce]=V[ce];return X},me=function(V){for(var z={},X=0;X0?ue.length===2?typeof ue[1]==s?this[ue[0]]=ue[1].call(this,ze):this[ue[0]]=ue[1]:ue.length===3?typeof ue[1]===s&&!(ue[1].exec&&ue[1].test)?this[ue[0]]=ze?ue[1].call(this,ze,ue[2]):n:this[ue[0]]=ze?ze.replace(ue[1],ue[2]):n:ue.length===4&&(this[ue[0]]=ze?ue[3].call(this,ze.replace(ue[1],ue[2])):n):this[ue]=ze||n;X+=2}},Be=function(V,z){for(var X in z)if(typeof z[X]===u&&z[X].length>0){for(var ce=0;ceI?Ae(Y,I):Y,this},this.setUA(X),this};ge.VERSION=i,ge.BROWSER=me([v,m,f]),ge.CPU=me([E]),ge.DEVICE=me([d,g,h,p,y,S,b,w,A]),ge.ENGINE=ge.OS=me([v,m]),t.exports&&(e=t.exports=ge),e.UAParser=ge;var Ne=typeof r!==l&&(r.jQuery||r.Zepto);if(Ne&&!Ne.ua){var nt=new ge;Ne.ua=nt.getResult(),Ne.ua.get=function(){return nt.getUA()},Ne.ua.set=function(V){nt.setUA(V);var z=nt.getResult();for(var X in z)Ne.ua[X]=z[X]}}})(typeof window=="object"?window:Cp)})(er,er.exports)),er.exports}var Pp=kp(),ne=function(){return ne=Object.assign||function(e){for(var r,n=1,i=arguments.length;n0&&o[o.length-1])&&(u[0]===6||u[0]===2)){r=0;continue}if(u[0]===3&&(!o||u[1]>o[0]&&u[1]=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function Ie(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),i,o=[],a;try{for(;(e===void 0||e-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(s){a={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return o}function De(t,e,r){if(arguments.length===2)for(var n=0,i=e.length,o;n=2&&h[1]&&(g=atob(h[1])),{deviceId:h[0],userId:g}}catch{return}},Op=function(t){var e=Fi(t,!0);try{var r=ye.localStorage.getItem(e);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},Rp=function(t){var e=Fi(t,!0);try{var r=ye.sessionStorage.getItem(e);if(!r)return;var n=JSON.parse(r);return typeof n!="object"?void 0:n}catch{return}},Fi=function(t,e){if(e)return t?.length<10?void 0:"AMP_".concat(t.substring(0,10));if(!(t?.length<6))return"amp_".concat(t.substring(0,6))},xp=(function(){function t(e,r,n){this.type="integration",this.apiKey=e,this.identityStore=r.identityStore,this.eventBridge=r.eventBridge,this.contextProvider=r.applicationContextProvider,this.timeoutMillis=n,this.loadPersistedState(),n<=0&&(this.setup=void 0)}return t.prototype.setup=function(e,r){return Ee(this,void 0,void 0,function(){return Se(this,function(n){return e?.automaticFetchOnAmplitudeIdentityChange&&this.identityStore.addIdentityListener(function(){r?.fetch()}),[2,this.waitForConnectorIdentity(this.timeoutMillis)]})})},t.prototype.getUser=function(){var e=this.identityStore.getIdentity();return{user_id:e.userId,device_id:e.deviceId,user_properties:e.userProperties,version:this.contextProvider.versionName}},t.prototype.track=function(e){return this.eventBridge.receiver?(this.eventBridge.logEvent({eventType:e.eventType,eventProperties:e.eventProperties}),!0):!1},t.prototype.loadPersistedState=function(){if(!this.apiKey||this.apiKey.startsWith("client-"))return!1;var e=Na(this.apiKey,!0);return e?(this.commitIdentityToConnector(e),!0):(e=Na(this.apiKey,!1),e?(this.commitIdentityToConnector(e),!0):(e=Op(this.apiKey),e?(this.commitIdentityToConnector(e),!0):(e=Rp(this.apiKey),e?(this.commitIdentityToConnector(e),!0):!1)))},t.prototype.commitIdentityToConnector=function(e){var r=this.identityStore.editIdentity();r.setDeviceId(e.deviceId),e.userId&&r.setUserId(e.userId),r.commit()},t.prototype.waitForConnectorIdentity=function(e){return Ee(this,void 0,void 0,function(){var r,n=this;return Se(this,function(i){return r=this.identityStore.getIdentity(),!r.userId&&!r.deviceId?[2,Promise.race([new Promise(function(o){var a=function(){o(),n.identityStore.removeIdentityListener(a)};n.identityStore.addIdentityListener(a)}),new Promise(function(o,a){ye.setTimeout(a,e,"Timed out waiting for Amplitude Analytics SDK to initialize.")})])]:[2]})})},t})();function Np(t,e){return e=e||{},new Promise(function(r,n){var i=new XMLHttpRequest,o=[],a=[],s={},l=function(){return{ok:(i.status/100|0)==2,statusText:i.statusText,status:i.status,url:i.responseURL,text:function(){return Promise.resolve(i.responseText)},json:function(){return Promise.resolve(JSON.parse(i.responseText))},blob:function(){return Promise.resolve(new Blob([i.response]))},clone:l,headers:{keys:function(){return o},entries:function(){return a},get:function(c){return s[c.toLowerCase()]},has:function(c){return c.toLowerCase()in s}}}};for(var u in i.open(e.method||"get",t,!0),i.onload=function(){i.getAllResponseHeaders().replace(/^(.*?):[^\S\n]*([\s\S]*?)$/gm,function(c,f,d){o.push(f=f.toLowerCase()),a.push([f,d]),s[f]=s[f]?s[f]+","+d:d}),r(l())},i.onerror=n,i.withCredentials=e.credentials=="include",e.headers)i.setRequestHeader(u,e.headers[u]);i.send(e.body||null)})}var Lp=ye.fetch||Np,Dp=function(t,e){return e==null||e<=0?t:new Promise(function(r,n){ye.setTimeout(function(){n(new hi("Request timeout after "+e+" milliseconds"))},e),t.then(r,n)})},Mp=function(t,e,r,n,i){var o=function(){return Ee(void 0,void 0,void 0,function(){var a,s,l;return Se(this,function(u){switch(u.label){case 0:return[4,Lp(t,{method:e,headers:r,body:n})];case 1:return a=u.sent(),l={status:a.status},[4,a.text()];case 2:return s=(l.body=u.sent(),l),[2,s]}})})};return Dp(o(),i)},Up=(function(){function t(e){this.client=e}return t.prototype.request=function(e){return Ee(this,void 0,void 0,function(){return Se(this,function(r){switch(r.label){case 0:return[4,this.client.request(e.requestUrl,e.method,e.headers,null,e.timeoutMillis)];case 1:return[2,r.sent()]}})})},t})(),Ju={request:Mp},He;(function(t){t[t.Disable=0]="Disable",t[t.Error=1]="Error",t[t.Warn=2]="Warn",t[t.Info=3]="Info",t[t.Debug=4]="Debug",t[t.Verbose=5]="Verbose"})(He||(He={}));var rt;(function(t){t.LocalStorage="localStorage",t.InitialVariants="initialVariants"})(rt||(rt={}));var be;(function(t){t.LocalStorage="storage",t.InitialVariants="initial",t.SecondaryLocalStorage="secondary-storage",t.SecondaryInitialVariants="secondary-initial",t.FallbackInline="fallback-inline",t.FallbackConfig="fallback-config",t.LocalEvaluation="local-evaluation"})(be||(be={}));var La=function(t){return!t||t===be.FallbackInline||t===be.FallbackConfig||t===be.SecondaryInitialVariants},Rt={debug:!1,logLevel:He.Error,loggerProvider:null,instanceName:"$default_instance",fallbackVariant:{},initialVariants:{},initialFlags:void 0,source:rt.LocalStorage,serverUrl:"https://api.lab.amplitude.com",flagsServerUrl:"https://flag.lab.amplitude.com",serverZone:"US",fetchTimeoutMillis:1e4,retryFetchOnFailure:!0,throwOnError:!1,automaticExposureTracking:!0,pollOnStart:!0,flagConfigPollingIntervalMillis:3e5,fetchOnStart:!0,automaticFetchOnAmplitudeIdentityChange:!1,userProvider:null,analyticsProvider:null,exposureTrackingProvider:null,httpClient:Ju},Da="1.20.4",Fp=512,qp=(function(){function t(e,r){var n=this,i;this.isReady=new Promise(function(a){n.resolve=a}),this.config=e,this.client=r;var o=(i=e.instanceName)!==null&&i!==void 0?i:Rt.instanceName;this.queue=new Vp(o),this.cache=new Bp(o)}return t.prototype.ready=function(){return this.integration?this.isReady:Promise.resolve()},t.prototype.setIntegration=function(e){var r=this;this.integration&&this.integration.teardown&&this.integration.teardown(),this.integration=e,e.setup?this.integration.setup(this.config,this.client).then(function(){r.queue.setTracker(r.integration.track.bind(e)),r.resolve()},function(){r.queue.setTracker(r.integration.track.bind(e)),r.resolve()}):(this.queue.setTracker(this.integration.track.bind(e)),this.resolve())},t.prototype.getUser=function(){return this.integration?this.integration.getUser():{}},t.prototype.track=function(e,r){if(this.cache.shouldTrack(e,r)){var n=this.getExposureEvent(e);this.queue.push(n)}},t.prototype.getExposureEvent=function(e){var r,n,i,o={eventType:"$exposure",eventProperties:e};return!((r=e.metadata)===null||r===void 0)&&r.exposureEvent?o={eventType:(n=e.metadata)===null||n===void 0?void 0:n.exposureEvent,eventProperties:e}:((i=e.metadata)===null||i===void 0?void 0:i.deliveryMethod)==="web"&&(o={eventType:"$impression",eventProperties:e}),o},t})(),Bp=(function(){function t(e){this.isSessionStorageAvailable=jp(),this.inMemoryCache={},this.identity={},this.storageKey="EXP_sent_v3_".concat(e),this.isSessionStorageAvailable&&(ye.sessionStorage.removeItem("EXP_sent_".concat(e)),ye.sessionStorage.removeItem("EXP_sent_v2_".concat(e)))}return t.prototype.shouldTrack=function(e,r){var n,i;if(((n=e.metadata)===null||n===void 0?void 0:n.deliveryMethod)==="web")return!0;var o={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,o)||this.clearCache(),this.identity=o,this.loadCache();var a=e.flag_key in this.inMemoryCache,s=this.inMemoryCache[e.flag_key],l=(i=e.variant)!==null&&i!==void 0?i:null,u=s??null,c=!1;return(!a||u!==l)&&(c=!0,this.inMemoryCache[e.flag_key]=l),this.storeCache(),c},t.prototype.clearCache=function(){this.inMemoryCache={},this.isSessionStorageAvailable&&ye.sessionStorage.removeItem(this.storageKey)},t.prototype.identityEquals=function(e,r){return e.userId&&r.userId?e.userId===r.userId:!e.userId&&!r.userId?e.deviceId===r.deviceId:!1},t.prototype.loadCache=function(){if(this.isSessionStorageAvailable){var e=ye.sessionStorage.getItem(this.storageKey);this.inMemoryCache=e?JSON.parse(e):{}}},t.prototype.storeCache=function(){if(this.isSessionStorageAvailable)try{ye.sessionStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryCache))}catch{}},t})(),Vp=(function(){function t(e,r){r===void 0&&(r=Fp),this.isLocalStorageAvailable=Ip(),this.inMemoryQueue=[],this.storageKey="EXP_unsent_".concat(e),this.maxQueueSize=r}return t.prototype.push=function(e){this.loadQueue(),this.inMemoryQueue.push(e),this.flush(),this.storeQueue()},t.prototype.setTracker=function(e){var r=this;this.tracker=e,this.poller=ye.setInterval(function(){r.loadFlushStore()},1e3),this.loadFlushStore()},t.prototype.flush=function(){if(this.tracker&&this.inMemoryQueue.length!==0){for(var e=0;ethis.maxQueueSize&&(this.inMemoryQueue=this.inMemoryQueue.slice(this.inMemoryQueue.length-this.maxQueueSize)),ye.localStorage.setItem(this.storageKey,JSON.stringify(this.inMemoryQueue)))},t.prototype.loadFlushStore=function(){this.loadQueue(),this.flush(),this.storeQueue()},t})(),jp=function(){var t=Tt();if(t)try{var e="EXP_test";return t.sessionStorage.setItem(e,e),t.sessionStorage.removeItem(e),!0}catch{return!1}return!1},Hp=(function(){function t(e,r){r===void 0&&(r=He.Error),this.logger=e,this.logLevel=r}return t.prototype.error=function(e){for(var r,n=[],i=1;i=He.Error&&(r=this.logger).error.apply(r,De([e],Ie(n),!1))},t.prototype.warn=function(e){for(var r,n=[],i=1;i=He.Warn&&(r=this.logger).warn.apply(r,De([e],Ie(n),!1))},t.prototype.info=function(e){for(var r,n=[],i=1;i=He.Info&&(r=this.logger).info.apply(r,De([e],Ie(n),!1))},t.prototype.debug=function(e){for(var r,n=[],i=1;i=He.Debug&&(r=this.logger).debug.apply(r,De([e],Ie(n),!1))},t.prototype.verbose=function(e){for(var r,n=[],i=1;i=He.Verbose&&(r=this.logger).verbose.apply(r,De([e],Ie(n),!1))},t})(),Gp=(function(){function t(){}return t.prototype.error=function(e){for(var r=[],n=1;n0&&f[0]){var d=f[0],v={group_name:d},h=(i=(n=t.group_properties)===null||n===void 0?void 0:n[c])===null||i===void 0?void 0:i[d];h&&Object.keys(h).length>0&&(v.group_properties=h),s[c]=v}}}catch(g){e={error:g}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(e)throw e.error}}return Object.keys(s).length>0&&(o.groups=s),delete o.user.groups,delete o.user.group_properties,o},Ce=function(t){return t==null?{}:typeof t=="string"?{key:t,value:t}:t},Ua=function(t){if(!t)return{};var e=void 0;t.metadata&&(e=t.metadata.experimentKey);var r={};return t.key&&(r.key=t.key),t.value&&(r.value=t.value),t.payload&&(r.payload=t.payload),e&&(r.expKey=e),t.metadata&&(r.metadata=t.metadata),r},Zp=(function(){function t(e){this.setProperties={},this.unsetProperties={},this.analyticsProvider=e}return t.prototype.track=function(e){this.setProperties[e.key]!=e.variant.value&&(this.setProperties[e.key]=e.variant.value,delete this.unsetProperties[e.key],this.analyticsProvider.track(e))},t.prototype.setUserProperty=function(e){this.setProperties[e.key]!=e.variant.value&&this.analyticsProvider.setUserProperty(e)},t.prototype.unsetUserProperty=function(e){this.unsetProperties[e.key]||(this.unsetProperties[e.key]="unset",delete this.setProperties[e.key],this.analyticsProvider.unsetUserProperty(e))},t})(),em=(function(){function t(e){this.tracked={},this.identity={},this.exposureTrackingProvider=e}return t.prototype.track=function(e,r){var n={userId:r?.user_id,deviceId:r?.device_id};this.identityEquals(this.identity,n)||(this.tracked={}),this.identity=n;var i=e.flag_key in this.tracked,o=this.tracked[e.flag_key];i&&o===e.variant||(this.tracked[e.flag_key]=e.variant,this.exposureTrackingProvider.track(e))},t.prototype.identityEquals=function(e,r){return e.userId===r.userId&&e.deviceId===r.deviceId},t})(),tm=1e4,rm=8,nm=500,im=1e4,om=1.5,Fa=6e4,am="https://api.lab.eu.amplitude.com",sm="https://flag.lab.eu.amplitude.com",um=(function(){function t(e,r){var n=this,i,o,a,s;this.engine=new dp,this.isRunning=!1,this.apiKey=e,r=Yp(r),this.config=ne(ne(ne({},Rt),r),{serverUrl:r?.serverUrl||(((i=r?.serverZone)===null||i===void 0?void 0:i.toLowerCase())==="eu"?am:Rt.serverUrl),flagsServerUrl:r?.flagsServerUrl||(((o=r?.serverZone)===null||o===void 0?void 0:o.toLowerCase())==="eu"?sm:Rt.flagsServerUrl),flagConfigPollingIntervalMillis:r.flagConfigPollingIntervalMillis=500||e.statusCode===429:!0},t.prototype.addPlugin=function(e){e.type==="integration"&&this.integrationManager.setIntegration(e)},t})(),lm=(function(){function t(e,r){var n,i,o;this.globalScope=Tt(),this.userAgent=typeof((n=this.globalScope)===null||n===void 0?void 0:n.navigator)<"u"?(i=this.globalScope)===null||i===void 0?void 0:i.navigator.userAgent:void 0,this.ua=new Pp.UAParser(this.userAgent).getResult(),this.localStorage=new un,this.sessionStorage=new el,this.userProvider=e,this.apiKey=r,this.storageKey="EXP_".concat((o=this.apiKey)===null||o===void 0?void 0:o.slice(0,10),"_DEFAULT_USER_PROVIDER")}return t.prototype.getUser=function(){var e,r,n,i,o,a=((e=this.userProvider)===null||e===void 0?void 0:e.getUser())||{};return ne({language:this.getLanguage(),platform:"Web",os:this.getOs(this.ua),device_model:this.getDeviceModel(this.ua),device_category:(n=(r=this.ua.device)===null||r===void 0?void 0:r.type)!==null&&n!==void 0?n:"desktop",referring_url:(o=(i=this.globalScope)===null||i===void 0?void 0:i.document)===null||o===void 0?void 0:o.referrer.replace(/\/$/,""),cookie:this.getCookie(),browser:this.getBrowser(this.ua),landing_url:this.getLandingUrl(),first_seen:this.getFirstSeen(),url_param:this.getUrlParam(),user_agent:this.userAgent},a)},t.prototype.getLanguage=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},t.prototype.getOs=function(e){var r,n;return[(r=e.browser)===null||r===void 0?void 0:r.name,(n=e.browser)===null||n===void 0?void 0:n.major].filter(function(i){return i!=null}).join(" ")},t.prototype.getDeviceModel=function(e){var r;return(r=e.os)===null||r===void 0?void 0:r.name},t.prototype.getBrowser=function(e){var r,n=(r=e.browser)===null||r===void 0?void 0:r.name;return n?.includes("Chrom")&&(n="Chrome"),n?.includes("Firefox")&&(n="Firefox"),n?.includes("Safari")&&(n="Safari"),n?.includes("Edge")&&(n="Edge"),n?.includes("Opera")&&(n="Opera"),n},t.prototype.getCookie=function(){var e,r,n,i,o;if(!((r=(e=this.globalScope)===null||e===void 0?void 0:e.document)===null||r===void 0)&&r.cookie)return Object.fromEntries((o=(i=(n=this.globalScope)===null||n===void 0?void 0:n.document)===null||i===void 0?void 0:i.cookie)===null||o===void 0?void 0:o.split("; ").map(function(a){return a.split("=")}))},t.prototype.getLandingUrl=function(){var e,r;try{var n=JSON.parse(this.sessionStorage.get(this.storageKey)||"{}");return n.landing_url||(n.landing_url=(r=(e=this.globalScope)===null||e===void 0?void 0:e.location)===null||r===void 0?void 0:r.href.replace(/\/$/,""),this.sessionStorage.put(this.storageKey,JSON.stringify(n))),n.landing_url}catch{return}},t.prototype.getFirstSeen=function(){try{var e=JSON.parse(this.localStorage.get(this.storageKey)||"{}");return e.first_seen||(e.first_seen=(Date.now()/1e3).toString(),this.localStorage.put(this.storageKey,JSON.stringify(e))),e.first_seen}catch{return}},t.prototype.getUrlParam=function(){var e,r,n;if(this.globalScope){var i={};try{var o=new URL(this.globalScope.location.href);try{for(var a=vt(o.searchParams),s=a.next();!s.done;s=a.next()){var l=Ie(s.value,2),u=l[0],c=l[1];i[u]=De(De([],Ie((n=i[u])!==null&&n!==void 0?n:[]),!1),Ie(c.split(",")),!1)}}catch(f){e={error:f}}finally{try{s&&!s.done&&(r=a.return)&&r.call(a)}finally{if(e)throw e.error}}}catch{return}return Object.entries(i).reduce(function(f,d){var v=Ie(d,2),h=v[0],g=v[1];return f[h]=g.length==1?g[0]:g,f},{})}},t})();ye.experimentInstances={};var qa=ye.experimentInstances,cm=function(t,e){var r=function(){return new xp(t,mi.getInstance(tl(e)),1e4)};return vm(t,e,r)},tl=function(t){return t?.instanceName||Rt.instanceName},dm=function(t,e){var r=tl(e),n=e?.internalInstanceNameSuffix;return n?"".concat(r,".").concat(t,".").concat(n):"".concat(r,".").concat(t)},fm=function(t,e){return new um(t,ne(ne({},e),{userProvider:new lm(e?.userProvider,t)}))},vm=function(t,e,r){var n=dm(t,e),i=qa[n];return i||(i=fm(t,e),r&&i.addPlugin(r()),qa[n]=i,i)},Vr=(function(){function t(e){this.name=t.pluginName,this.config=e}return t.prototype.setup=function(e,r){var n;return _(this,void 0,void 0,function(){return T(this,function(i){return this.experiment=cm(((n=this.config)===null||n===void 0?void 0:n.deploymentKey)||e.apiKey,this.config),[2]})})},t.pluginName="@amplitude/experiment-analytics-plugin",t})(),hm=function(t){return new Vr(t)},gm=(function(){function t(){}return t.prototype.getApplicationContext=function(){return{versionName:this.versionName,language:pm(),platform:"Web",os:void 0,deviceModel:void 0}},t})(),pm=function(){return typeof navigator<"u"&&(navigator.languages&&navigator.languages[0]||navigator.language)||""},mm=(function(){function t(){this.queue=[]}return t.prototype.logEvent=function(e){this.receiver?this.receiver(e):this.queue.length<512&&this.queue.push(e)},t.prototype.setEventReceiver=function(e){this.receiver=e,this.queue.length>0&&(this.queue.forEach(function(r){e(r)}),this.queue=[])},t})(),ut=function(){return ut=Object.assign||function(t){for(var e,r=1,n=arguments.length;r=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function Ba(t,e){var r=typeof Symbol=="function"&&t[Symbol.iterator];if(!r)return t;var n=r.call(t),i,o=[],a;try{for(;(e===void 0||e-- >0)&&!(i=n.next()).done;)o.push(i.value)}catch(s){a={error:s}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return o}var Hr=function(t,e){var r,n,i=["string","number","boolean","undefined"],o=typeof t,a=typeof e;if(o!==a)return!1;try{for(var s=jr(i),l=s.next();!l.done;l=s.next()){var u=l.value;if(u===o)return t===e}}catch(m){r={error:m}}finally{try{l&&!l.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}if(t==null&&e==null)return!0;if(t==null||e==null||t.length!==e.length)return!1;var c=Array.isArray(t),f=Array.isArray(e);if(c!==f)return!1;if(c&&f){for(var d=0;d{if(f=!0,S&&(clearTimeout(S),S=null),d._q&&d._q.length>0)for(console.warn(`Engagement SDK failed to load within ${A}ms. Resolving pending calls gracefully.`);d._q.length>0;){let I=d._q.shift();if(!I)continue;let k=I[0],x=Va.includes(k);if(console.warn(`Engagement SDK method '${k}' still in queue (isAsyncMethod=${x}); attempting to resolve as no-op.`),x&&I[1]instanceof Function&&I[2]instanceof Function){let P=I[1];console.warn(`Engagement SDK method '${k}' resolved as no-op due to script loading failure`),P(void 0)}}};t(b,d._configuration.options.splitting?"module":void 0,g?.nonce,w);let A=1e4;S=setTimeout(()=>{w()},A)},plugin(h){let g=d.init;return{name:"@amplitude/engagement-browser",type:"enrichment",async setup(m,E){var p;let y=(p=m.instanceName)!=null?p:Tm,b=wm.getInstance(y).identityStore;g(m.apiKey,{serverZone:m.serverZone,...h,options:{logLevel:m.logLevel,logger:m.loggerProvider,...h?.options}});let S=[{track:w=>{E.track(w)}}];await window.engagement.boot({user:()=>{let w=b.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:w.userProperties,getSessionId:E.getSessionId}},integrations:S}),b.addIdentityListener(w=>{var A,I,k,x;if(!((A=window.engagement)!=null&&A._.user)||!((I=window.engagement)!=null&&I._analytics.hasBooted)){console.warn("Engagement SDK not booted. Ignoring identity change.");return}((x=(k=window.engagement)==null?void 0:k._.user)==null?void 0:x.user_id)!==w.userId?(window.engagement.shutdown(),window.engagement.boot({user:()=>{let P=b.getIdentity();return{user_id:E.getUserId(),device_id:E.getDeviceId(),user_properties:P.userProperties,getSessionId:E.getSessionId}},integrations:S})):window.engagement._setUserProperties(w.userProperties)})},async execute(m){return window.engagement.forwardEvent(m),m}}}},v=d;return new Proxy(d,{get:function(h,g){if(g in v)return v[g];if(g!=="then")return g==="gs"||g==="rc"?new Proxy({},{get:function(m,E){return function(){let p=Array.from(arguments),y=`${g}.${E}`;p.unshift(y),d._q.push(p)}}}):Va.includes(g)?function(){let m=Array.prototype.slice.call(arguments);return new Promise((E,p)=>{m.unshift(g,E,p),d._q.push(m),f&&E(void 0)})}:function(){let m=Array.prototype.slice.call(arguments);m.unshift(g),d._q.push(m)}}})}var rl=(t=>(t.Local="https://local.amplitude.com:3010/",t.Staging="https://apps.stag2.amplitude.com/",t.Production="https://app.amplitude.com/",t.ProductionEU="https://app.eu.amplitude.com/",t))(rl||{}),Im=t=>{if(typeof window<"u"&&window.opener)for(let e of Object.values(rl))window.opener.postMessage({message:t},e)};function Am(t,e,r,n){for(let i of t){let o=document.createElement("script");o.src=i,o.id="engagement-sdk-bundle",r&&o.setAttribute("nonce",r),e&&o.setAttribute("type",e),n&&(o.onerror=n),document.getElementsByTagName("head")[0].appendChild(o)}}var Cm=()=>(Im("ENGAGEMENT_SNIPPET"),_m((t,e,r,n)=>Am([t||"https://cdn.amplitude.com/engagement-browser/prod/index.min.js.gz"],e,r,n))),ja=Cm(),km=t=>(typeof window<"u"&&!window.engagement&&(window.engagement=ja),ja.plugin(t)),Pm="1.0.15",Om="amplitude-ts-unified",Rm=function(){return{type:"enrichment",name:"@amplitude/unified-library-plugin",execute:function(t){var e;return _(this,void 0,void 0,function(){return T(this,function(r){return t.library="".concat(Om,"/").concat(Pm,"-").concat((e=t.library)!==null&&e!==void 0?e:""),[2,t]})})}}},xm=(function(t){Fe(e,t);function e(){return t!==null&&t.apply(this,arguments)||this}return e.prototype.sessionReplay=function(){return this._sessionReplay},e.prototype.experiment=function(){if(this.config!==void 0){var r=this.plugins(Vr);if(r.length===0){this.config.loggerProvider.debug("".concat(Vr.pluginName," plugin is not found."));return}else{if(r.length===1)return r[0].experiment;this.config.loggerProvider.debug("Multiple instances of ".concat(Vr.pluginName," are found."));return}}},e.prototype.initAll=function(r,n){return _(this,void 0,void 0,function(){var i=this;return T(this,function(o){return this._initAllPromise?[2,this._initAllPromise]:(this._initAllPromise=(function(){return _(i,void 0,void 0,function(){var a,s;return T(this,function(l){switch(l.label){case 0:return a={serverZone:n?.serverZone,instanceName:n?.instanceName},t.prototype.add.call(this,Rm()),[4,t.prototype.init.call(this,r,C(C({},n?.analytics),a)).promise];case 1:return l.sent(),[4,t.prototype.add.call(this,Yg(C(C({},n?.sessionReplay),a))).promise];case 2:return l.sent(),[4,t.prototype.add.call(this,hm(C(C({},n?.experiment),a))).promise];case 3:return l.sent(),s=this.plugin(li.pluginName),s===void 0?this.config.loggerProvider.debug("".concat(li.pluginName," plugin is not found.")):this._sessionReplay=s.sessionReplay,[4,t.prototype.add.call(this,km(C(C({},n?.engagement),a))).promise];case 4:return l.sent(),[2]}})})})().finally(function(){i._initAllPromise=void 0}),[2,this._initAllPromise])})})},e.prototype.init=function(r,n,i){r===void 0&&(r="");var o=t.prototype.init.call(this,r,n,i);return o},e})(ih),Nm=function(){var t=new xm;return{_setDiagnosticsSampleRate:Q(t._setDiagnosticsSampleRate.bind(t),"_setDiagnosticsSampleRate",ae(t),se(t,["config"])),_enableRequestBodyCompressionExperimental:Q(t._enableRequestBodyCompressionExperimental.bind(t),"_enableRequestBodyCompressionExperimental",ae(t),se(t,["config"])),experiment:Q(t.experiment.bind(t),"experiment",ae(t),se(t,["config"])),sessionReplay:Q(t.sessionReplay.bind(t),"sessionReplay",ae(t),se(t,["config"])),initAll:Q(t.initAll.bind(t),"initAll",ae(t),se(t,["config"])),init:Q(t.init.bind(t),"init",ae(t),se(t,["config"])),add:Q(t.add.bind(t),"add",ae(t),se(t,["config.apiKey","timeline.plugins"])),remove:Q(t.remove.bind(t),"remove",ae(t),se(t,["config.apiKey","timeline.plugins"])),track:Q(t.track.bind(t),"track",ae(t),se(t,["config.apiKey","timeline.queue.length"])),logEvent:Q(t.logEvent.bind(t),"logEvent",ae(t),se(t,["config.apiKey","timeline.queue.length"])),identify:Q(t.identify.bind(t),"identify",ae(t),se(t,["config.apiKey","timeline.queue.length"])),groupIdentify:Q(t.groupIdentify.bind(t),"groupIdentify",ae(t),se(t,["config.apiKey","timeline.queue.length"])),setGroup:Q(t.setGroup.bind(t),"setGroup",ae(t),se(t,["config.apiKey","timeline.queue.length"])),revenue:Q(t.revenue.bind(t),"revenue",ae(t),se(t,["config.apiKey","timeline.queue.length"])),flush:Q(t.flush.bind(t),"flush",ae(t),se(t,["config.apiKey","timeline.queue.length"])),getUserId:Q(t.getUserId.bind(t),"getUserId",ae(t),se(t,["config","config.userId"])),setUserId:Q(t.setUserId.bind(t),"setUserId",ae(t),se(t,["config","config.userId"])),getDeviceId:Q(t.getDeviceId.bind(t),"getDeviceId",ae(t),se(t,["config","config.deviceId"])),setDeviceId:Q(t.setDeviceId.bind(t),"setDeviceId",ae(t),se(t,["config","config.deviceId"])),reset:Q(t.reset.bind(t),"reset",ae(t),se(t,["config","config.userId","config.deviceId"])),getSessionId:Q(t.getSessionId.bind(t),"getSessionId",ae(t),se(t,["config"])),setSessionId:Q(t.setSessionId.bind(t),"setSessionId",ae(t),se(t,["config"])),extendSession:Q(t.extendSession.bind(t),"extendSession",ae(t),se(t,["config"])),setOptOut:Q(t.setOptOut.bind(t),"setOptOut",ae(t),se(t,["config"])),getOptOut:Q(t.getOptOut.bind(t),"getOptOut",ae(t),se(t,["config"])),setTransport:Q(t.setTransport.bind(t),"setTransport",ae(t),se(t,["config"])),getIdentity:Q(t.getIdentity.bind(t),"getIdentity",ae(t),se(t,["config"])),setIdentity:Q(t.setIdentity.bind(t),"setIdentity",ae(t),se(t,["config","config.userId","config.deviceId"]))}};const gr=Nm();var Fm=gr.initAll,qm=gr.identify,Bm=gr.reset,Vm=gr.setUserId,jm=gr.track;export{gl as B,tr as I,_,T as a,qm as b,C as c,Fm as d,Qt as e,F as g,Dm as i,Tu as o,Bm as r,Vm as s,jm as t}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/K4hkKKa0.js - -```js -import{b as u,g as c,d as l,j as g,k as h}from"./3TW-MQft.js";import{s as d,I as f,b as m,t as o,r as b}from"./HWAjEwF5.js";const y=!0,n=Symbol("auth");class S{#t=u(null);get user(){return c(this.#t)}set user(t){l(this.#t,t,!0)}constructor(){{const t=localStorage.getItem("currentUser");t&&(this.user={username:t,burritoConsiderations:0})}}login=async(t,i)=>{try{const e=await fetch("/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({username:t,password:i})});if(e.ok){const{user:a}=await e.json();if(this.user=a,y){localStorage.setItem("currentUser",t),d(t);const s=new f;s.set("username",t),m(s),o("user_logged_in",{username:t})}return!0}return!1}catch(e){return console.error("Login error:",e),!1}};logout=()=>{o("user_logged_out"),b(),localStorage.removeItem("currentUser"),this.user=null};incrementBurritoConsiderations=()=>{this.user&&(this.user={...this.user,burritoConsiderations:this.user.burritoConsiderations+1})}}function I(r){g(n,r)}function U(){return h(n)}export{S as A,U as g,I as s}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/chunks/sPdhR0i8.js - -```js -var me={},ki=Object.defineProperty,qi=(e,o,p)=>o in e?ki(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,le=(e,o,p)=>qi(e,typeof o!="symbol"?o+"":o,p),Fi=Object.defineProperty,Bi=(e,o,p)=>o in e?Fi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,_e=(e,o,p)=>Bi(e,typeof o!="symbol"?o+"":o,p),tr,zi=Object.defineProperty,ji=(e,o,p)=>o in e?zi(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,rr=(e,o,p)=>ji(e,typeof o!="symbol"?o+"":o,p);const ir={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},sr={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},ae={};function Wi(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function kt(e){if(ae[e])return ae[e];const o=Wi(e)||globalThis[e],p=o.prototype,d=e in ir?ir[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in sr?sr[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return ae[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?ae[e]=h:p}catch{return p}}const Ie={};function W(e,o,p){var d;const s=`${e}.${String(p)}`;if(Ie[s])return Ie[s].call(o);const f=kt(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(Ie[s]=a,a.call(o)):o[p]}const Le={};function pi(e,o,p){const d=`${e}.${String(p)}`;if(Le[d])return Le[d].bind(o);const f=kt(e)[p];return typeof f!="function"?o[p]:(Le[d]=f,f.bind(o))}function Hi(e){return W("Node",e,"childNodes")}function Gi(e){return W("Node",e,"parentNode")}function Ji(e){return W("Node",e,"parentElement")}function Vi(e){return W("Node",e,"textContent")}function Ki(e,o){return pi("Node",e,"contains")(o)}function Qi(e){return pi("Node",e,"getRootNode")()}function Yi(e){return!e||!("host"in e)?null:W("ShadowRoot",e,"host")}function Xi(e){return e.styleSheets}function Zi(e){return!e||!("shadowRoot"in e)?null:W("Element",e,"shadowRoot")}function es(e,o){return W("Element",e,"querySelector")(o)}function ts(e,o){return W("Element",e,"querySelectorAll")(o)}function rs(){return kt("MutationObserver").constructor}const ge={childNodes:Hi,parentNode:Gi,parentElement:Ji,textContent:Vi,contains:Ki,getRootNode:Qi,host:Yi,styleSheets:Xi,shadowRoot:Zi,querySelector:es,querySelectorAll:ts,mutationObserver:rs};function is(e){const o=e&&"host"in e&&"mode"in e&&ge.host(e)||null;return!!(o&&"shadowRoot"in o&&ge.shadowRoot(o)===e)}class ss{constructor(){rr(this,"idNodeMap",new Map),rr(this,"nodeMetaMap",new WeakMap)}getId(o){var p;return o?((p=this.getMeta(o))==null?void 0:p.id)??-1:-1}getNode(o){return this.idNodeMap.get(o)||null}getIds(){return Array.from(this.idNodeMap.keys())}getMeta(o){return this.nodeMetaMap.get(o)||null}removeNodeFromMap(o){const p=this.getId(o);this.idNodeMap.delete(p),o.childNodes&&o.childNodes.forEach(d=>this.removeNodeFromMap(d))}has(o){return this.idNodeMap.has(o)}hasNode(o){return this.nodeMetaMap.has(o)}add(o,p){const d=p.id;this.idNodeMap.set(d,o),this.nodeMetaMap.set(o,p)}replace(o,p){const d=this.getNode(o);if(d){const s=this.nodeMetaMap.get(d);s&&this.nodeMetaMap.set(p,s)}this.idNodeMap.set(o,p)}reset(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}}function ns(){return new ss}const os=-2;function Tt(e,o,p){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return p?Tt(ge.parentNode(e),o,p):!1;for(let d=e.classList.length;d--;){const s=e.classList[d];if(o.test(s))return!0}return p?Tt(ge.parentNode(e),o,p):!1}function ls(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function as(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var ue={exports:{}},nr;function us(){if(nr)return ue.exports;nr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return ue.exports=o(),ue.exports.createColors=o,ue.exports}const fs={},hs=Object.freeze(Object.defineProperty({__proto__:null,default:fs},Symbol.toStringTag,{value:"Module"})),F=as(hs);var $e,or;function qt(){if(or)return $e;or=1;let e=us(),o=F;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return $e=p,p.default=p,$e}var fe={},lr;function Ft(){return lr||(lr=1,fe.isClean=Symbol("isClean"),fe.my=Symbol("my")),fe}var Te,ar;function di(){if(ar)return Te;ar=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return Te=p,p.default=p,Te}var Ue,ur;function ye(){if(ur)return Ue;ur=1;let e=di();function o(p,d){new e(d).stringify(p)}return Ue=o,o.default=o,Ue}var De,fr;function we(){if(fr)return De;fr=1;let{isClean:e,my:o}=Ft(),p=qt(),d=di(),s=ye();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return De=a,a.default=a,De}var ke,hr;function be(){if(hr)return ke;hr=1;let e=we();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ke=o,o.default=o,ke}var qe,cr;function cs(){if(cr)return qe;cr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return qe={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},qe}var Fe,pr;function mi(){if(pr)return Fe;pr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{existsSync:p,readFileSync:d}=F,{dirname:s,join:f}=F;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return Fe=m,m.default=m,Fe}var Be,dr;function ve(){if(dr)return Be;dr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{fileURLToPath:p,pathToFileURL:d}=F,{isAbsolute:s,resolve:f}=F,{nanoid:a}=cs(),m=F,n=qt(),h=mi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return Be=r,r.default=r,m&&m.registerInput&&m.registerInput(r),Be}var ze,mr;function gi(){if(mr)return ze;mr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=F,{dirname:p,relative:d,resolve:s,sep:f}=F,{pathToFileURL:a}=F,m=ve(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return ze=t,ze}var je,gr;function xe(){if(gr)return je;gr=1;let e=we();class o extends e{constructor(d){super(d),this.type="comment"}}return je=o,o.default=o,je}var We,yr;function Q(){if(yr)return We;yr=1;let{isClean:e,my:o}=Ft(),p=be(),d=xe(),s=we(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},We=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},We}var He,wr;function Bt(){if(wr)return He;wr=1;let e=Q(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},He=d,d.default=d,He}var Ge,br;function yi(){if(br)return Ge;br=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return Ge=e,e.default=e,Ge}var Je,vr;function zt(){if(vr)return Je;vr=1;let e=yi();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return Je=o,o.default=o,Je}var Ve,xr;function ps(){if(xr)return Ve;xr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return Ve=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},Ve}var Ke,Sr;function jt(){if(Sr)return Ke;Sr=1;let e=Q();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return Ke=o,o.default=o,e.registerAtRule(o),Ke}var Qe,Rr;function ne(){if(Rr)return Qe;Rr=1;let e=Q(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},Qe=d,d.default=d,e.registerRoot(d),Qe}var Ye,Or;function wi(){if(Or)return Ye;Or=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Ye=e,e.default=e,Ye}var Xe,Cr;function Wt(){if(Cr)return Xe;Cr=1;let e=Q(),o=wi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Xe=p,p.default=p,e.registerRule(p),Xe}var Ze,Er;function ds(){if(Er)return Ze;Er=1;let e=be(),o=ps(),p=xe(),d=jt(),s=ne(),f=Wt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},tt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),tt}var rt,Nr;function ms(){if(Nr)return rt;Nr=1;let e=gi(),o=ye(),p=Ht();const d=zt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return rt=s,s.default=s,rt}var it,Mr;function gs(){if(Mr)return it;Mr=1;let e=ms(),o=bi(),p=Bt(),d=ne();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return it=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),it}var st,_r;function ys(){if(_r)return st;_r=1;let e=be(),o=mi(),p=xe(),d=jt(),s=ve(),f=ne(),a=Wt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return st=m,m.default=m,st}var nt,Ir;function ws(){if(Ir)return nt;Ir=1;let e=qt(),o=be(),p=bi(),d=Q(),s=gs(),f=ye(),a=ys(),m=Bt(),n=yi(),h=xe(),t=jt(),l=zt(),i=ve(),r=Ht(),c=wi(),u=Wt(),g=ne(),S=we();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),nt=b,b.default=b,nt}var bs=ws();const _=ls(bs);_.stringify;_.fromJSON;_.plugin;_.parse;_.list;_.document;_.comment;_.atRule;_.rule;_.decl;_.root;_.CssSyntaxError;_.Declaration;_.Container;_.Processor;_.Document;_.Comment;_.Warning;_.AtRule;_.Result;_.Input;_.Rule;_.Root;_.Node;var vs=Object.defineProperty,xs=(e,o,p)=>o in e?vs(e,o,{enumerable:!0,configurable:!0,writable:!0,value:p}):e[o]=p,k=(e,o,p)=>xs(e,typeof o!="symbol"?o+"":o,p);function Ss(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Rs(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var o=e.default;if(typeof o=="function"){var p=function d(){return this instanceof d?Reflect.construct(o,arguments,this.constructor):o.apply(this,arguments)};p.prototype=o.prototype}else p={};return Object.defineProperty(p,"__esModule",{value:!0}),Object.keys(e).forEach(function(d){var s=Object.getOwnPropertyDescriptor(e,d);Object.defineProperty(p,d,s.get?s:{enumerable:!0,get:function(){return e[d]}})}),p}var he={exports:{}},Lr;function Os(){if(Lr)return he.exports;Lr=1;var e=String,o=function(){return{isColorSupported:!1,reset:e,bold:e,dim:e,italic:e,underline:e,inverse:e,hidden:e,strikethrough:e,black:e,red:e,green:e,yellow:e,blue:e,magenta:e,cyan:e,white:e,gray:e,bgBlack:e,bgRed:e,bgGreen:e,bgYellow:e,bgBlue:e,bgMagenta:e,bgCyan:e,bgWhite:e}};return he.exports=o(),he.exports.createColors=o,he.exports}const Cs={},Es=Object.freeze(Object.defineProperty({__proto__:null,default:Cs},Symbol.toStringTag,{value:"Module"})),B=Rs(Es);var ot,$r;function Gt(){if($r)return ot;$r=1;let e=Os(),o=B;class p extends Error{constructor(s,f,a,m,n,h){super(s),this.name="CssSyntaxError",this.reason=s,n&&(this.file=n),m&&(this.source=m),h&&(this.plugin=h),typeof f<"u"&&typeof a<"u"&&(typeof f=="number"?(this.line=f,this.column=a):(this.line=f.line,this.column=f.column,this.endLine=a.line,this.endColumn=a.column)),this.setMessage(),Error.captureStackTrace&&Error.captureStackTrace(this,p)}setMessage(){this.message=this.plugin?this.plugin+": ":"",this.message+=this.file?this.file:"",typeof this.line<"u"&&(this.message+=":"+this.line+":"+this.column),this.message+=": "+this.reason}showSourceCode(s){if(!this.source)return"";let f=this.source;s==null&&(s=e.isColorSupported),o&&s&&(f=o(f));let a=f.split(/\r?\n/),m=Math.max(this.line-3,0),n=Math.min(this.line+2,a.length),h=String(n).length,t,l;if(s){let{bold:i,gray:r,red:c}=e.createColors(!0);t=u=>i(c(u)),l=u=>r(u)}else t=l=i=>i;return a.slice(m,n).map((i,r)=>{let c=m+1+r,u=" "+(" "+c).slice(-h)+" | ";if(c===this.line){let g=l(u.replace(/\d/g," "))+i.slice(0,this.column-1).replace(/[^\t]/g," ");return t(">")+l(u)+i+` - `+g+t("^")}return" "+l(u)+i}).join(` -`)}toString(){let s=this.showSourceCode();return s&&(s=` - -`+s+` -`),this.name+": "+this.message+s}}return ot=p,p.default=p,ot}var ce={},Tr;function Jt(){return Tr||(Tr=1,ce.isClean=Symbol("isClean"),ce.my=Symbol("my")),ce}var lt,Ur;function vi(){if(Ur)return lt;Ur=1;const e={after:` -`,beforeClose:` -`,beforeComment:` -`,beforeDecl:` -`,beforeOpen:" ",beforeRule:` -`,colon:": ",commentLeft:" ",commentRight:" ",emptyBody:"",indent:" ",semicolon:!1};function o(d){return d[0].toUpperCase()+d.slice(1)}class p{constructor(s){this.builder=s}atrule(s,f){let a="@"+s.name,m=s.params?this.rawValue(s,"params"):"";if(typeof s.raws.afterName<"u"?a+=s.raws.afterName:m&&(a+=" "),s.nodes)this.block(s,a+m);else{let n=(s.raws.between||"")+(f?";":"");this.builder(a+m+n,s)}}beforeAfter(s,f){let a;s.type==="decl"?a=this.raw(s,null,"beforeDecl"):s.type==="comment"?a=this.raw(s,null,"beforeComment"):f==="before"?a=this.raw(s,null,"beforeRule"):a=this.raw(s,null,"beforeClose");let m=s.parent,n=0;for(;m&&m.type!=="root";)n+=1,m=m.parent;if(a.includes(` -`)){let h=this.raw(s,null,"indent");if(h.length)for(let t=0;t0&&s.nodes[f].type==="comment";)f-=1;let a=this.raw(s,"semicolon");for(let m=0;m{if(m=l.raws[f],typeof m<"u")return!1})}return typeof m>"u"&&(m=e[a]),h.rawCache[a]=m,m}rawBeforeClose(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length>0&&typeof a.raws.after<"u")return f=a.raws.after,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawBeforeComment(s,f){let a;return s.walkComments(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeDecl"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeDecl(s,f){let a;return s.walkDecls(m=>{if(typeof m.raws.before<"u")return a=m.raws.before,a.includes(` -`)&&(a=a.replace(/[^\n]+$/,"")),!1}),typeof a>"u"?a=this.raw(f,null,"beforeRule"):a&&(a=a.replace(/\S/g,"")),a}rawBeforeOpen(s){let f;return s.walk(a=>{if(a.type!=="decl"&&(f=a.raws.between,typeof f<"u"))return!1}),f}rawBeforeRule(s){let f;return s.walk(a=>{if(a.nodes&&(a.parent!==s||s.first!==a)&&typeof a.raws.before<"u")return f=a.raws.before,f.includes(` -`)&&(f=f.replace(/[^\n]+$/,"")),!1}),f&&(f=f.replace(/\S/g,"")),f}rawColon(s){let f;return s.walkDecls(a=>{if(typeof a.raws.between<"u")return f=a.raws.between.replace(/[^\s:]/g,""),!1}),f}rawEmptyBody(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length===0&&(f=a.raws.after,typeof f<"u"))return!1}),f}rawIndent(s){if(s.raws.indent)return s.raws.indent;let f;return s.walk(a=>{let m=a.parent;if(m&&m!==s&&m.parent&&m.parent===s&&typeof a.raws.before<"u"){let n=a.raws.before.split(` -`);return f=n[n.length-1],f=f.replace(/\S/g,""),!1}}),f}rawSemicolon(s){let f;return s.walk(a=>{if(a.nodes&&a.nodes.length&&a.last.type==="decl"&&(f=a.raws.semicolon,typeof f<"u"))return!1}),f}rawValue(s,f){let a=s[f],m=s.raws[f];return m&&m.value===a?m.raw:a}root(s){this.body(s),s.raws.after&&this.builder(s.raws.after)}rule(s){this.block(s,this.rawValue(s,"selector")),s.raws.ownSemicolon&&this.builder(s.raws.ownSemicolon,s,"end")}stringify(s,f){if(!this[s.type])throw new Error("Unknown AST node type "+s.type+". Maybe you need to change PostCSS stringifier.");this[s.type](s,f)}}return lt=p,p.default=p,lt}var at,Dr;function Se(){if(Dr)return at;Dr=1;let e=vi();function o(p,d){new e(d).stringify(p)}return at=o,o.default=o,at}var ut,kr;function Re(){if(kr)return ut;kr=1;let{isClean:e,my:o}=Jt(),p=Gt(),d=vi(),s=Se();function f(m,n){let h=new m.constructor;for(let t in m){if(!Object.prototype.hasOwnProperty.call(m,t)||t==="proxyCache")continue;let l=m[t],i=typeof l;t==="parent"&&i==="object"?n&&(h[t]=n):t==="source"?h[t]=l:Array.isArray(l)?h[t]=l.map(r=>f(r,h)):(i==="object"&&l!==null&&(l=f(l)),h[t]=l)}return h}class a{constructor(n={}){this.raws={},this[e]=!1,this[o]=!0;for(let h in n)if(h==="nodes"){this.nodes=[];for(let t of n[h])typeof t.clone=="function"?this.append(t.clone()):this.append(t)}else this[h]=n[h]}addToError(n){if(n.postcssNode=this,n.stack&&this.source&&/\n\s{4}at /.test(n.stack)){let h=this.source;n.stack=n.stack.replace(/\n\s{4}at /,`$&${h.input.from}:${h.start.line}:${h.start.column}$&`)}return n}after(n){return this.parent.insertAfter(this,n),this}assign(n={}){for(let h in n)this[h]=n[h];return this}before(n){return this.parent.insertBefore(this,n),this}cleanRaws(n){delete this.raws.before,delete this.raws.after,n||delete this.raws.between}clone(n={}){let h=f(this);for(let t in n)h[t]=n[t];return h}cloneAfter(n={}){let h=this.clone(n);return this.parent.insertAfter(this,h),h}cloneBefore(n={}){let h=this.clone(n);return this.parent.insertBefore(this,h),h}error(n,h={}){if(this.source){let{end:t,start:l}=this.rangeBy(h);return this.source.input.error(n,{column:l.column,line:l.line},{column:t.column,line:t.line},h)}return new p(n)}getProxyProcessor(){return{get(n,h){return h==="proxyOf"?n:h==="root"?()=>n.root().toProxy():n[h]},set(n,h,t){return n[h]===t||(n[h]=t,(h==="prop"||h==="value"||h==="name"||h==="params"||h==="important"||h==="text")&&n.markDirty()),!0}}}markDirty(){if(this[e]){this[e]=!1;let n=this;for(;n=n.parent;)n[e]=!1}}next(){if(!this.parent)return;let n=this.parent.index(this);return this.parent.nodes[n+1]}positionBy(n,h){let t=this.source.start;if(n.index)t=this.positionInside(n.index,h);else if(n.word){h=this.toString();let l=h.indexOf(n.word);l!==-1&&(t=this.positionInside(l,h))}return t}positionInside(n,h){let t=h||this.toString(),l=this.source.start.column,i=this.source.start.line;for(let r=0;rtypeof u=="object"&&u.toJSON?u.toJSON(null,h):u);else if(typeof c=="object"&&c.toJSON)t[r]=c.toJSON(null,h);else if(r==="source"){let u=h.get(c.input);u==null&&(u=i,h.set(c.input,i),i++),t[r]={end:c.end,inputId:u,start:c.start}}else t[r]=c}return l&&(t.inputs=[...h.keys()].map(r=>r.toJSON())),t}toProxy(){return this.proxyCache||(this.proxyCache=new Proxy(this,this.getProxyProcessor())),this.proxyCache}toString(n=s){n.stringify&&(n=n.stringify);let h="";return n(this,t=>{h+=t}),h}warn(n,h,t){let l={node:this};for(let i in t)l[i]=t[i];return n.warn(h,l)}get proxyOf(){return this}}return ut=a,a.default=a,ut}var ft,qr;function Oe(){if(qr)return ft;qr=1;let e=Re();class o extends e{constructor(d){d&&typeof d.value<"u"&&typeof d.value!="string"&&(d={...d,value:String(d.value)}),super(d),this.type="decl"}get variable(){return this.prop.startsWith("--")||this.prop[0]==="$"}}return ft=o,o.default=o,ft}var ht,Fr;function Ps(){if(Fr)return ht;Fr=1;let e="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";return ht={nanoid:(d=21)=>{let s="",f=d;for(;f--;)s+=e[Math.random()*64|0];return s},customAlphabet:(d,s=21)=>(f=s)=>{let a="",m=f;for(;m--;)a+=d[Math.random()*d.length|0];return a}},ht}var ct,Br;function xi(){if(Br)return ct;Br=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{existsSync:p,readFileSync:d}=B,{dirname:s,join:f}=B;function a(n){return Buffer?Buffer.from(n,"base64").toString():window.atob(n)}class m{constructor(h,t){if(t.map===!1)return;this.loadAnnotation(h),this.inline=this.startWith(this.annotation,"data:");let l=t.map?t.map.prev:void 0,i=this.loadMap(t.from,l);!this.mapFile&&t.from&&(this.mapFile=t.from),this.mapFile&&(this.root=s(this.mapFile)),i&&(this.text=i)}consumer(){return this.consumerCache||(this.consumerCache=new e(this.text)),this.consumerCache}decodeInline(h){let t=/^data:application\/json;charset=utf-?8;base64,/,l=/^data:application\/json;base64,/,i=/^data:application\/json;charset=utf-?8,/,r=/^data:application\/json,/;if(i.test(h)||r.test(h))return decodeURIComponent(h.substr(RegExp.lastMatch.length));if(t.test(h)||l.test(h))return a(h.substr(RegExp.lastMatch.length));let c=h.match(/data:application\/json;([^,]+),/)[1];throw new Error("Unsupported source map encoding "+c)}getAnnotationURL(h){return h.replace(/^\/\*\s*# sourceMappingURL=/,"").trim()}isMap(h){return typeof h!="object"?!1:typeof h.mappings=="string"||typeof h._mappings=="string"||Array.isArray(h.sections)}loadAnnotation(h){let t=h.match(/\/\*\s*# sourceMappingURL=/gm);if(!t)return;let l=h.lastIndexOf(t.pop()),i=h.indexOf("*/",l);l>-1&&i>-1&&(this.annotation=this.getAnnotationURL(h.substring(l,i)))}loadFile(h){if(this.root=s(h),p(h))return this.mapFile=h,d(h,"utf-8").toString().trim()}loadMap(h,t){if(t===!1)return!1;if(t){if(typeof t=="string")return t;if(typeof t=="function"){let l=t(h);if(l){let i=this.loadFile(l);if(!i)throw new Error("Unable to load previous source map: "+l.toString());return i}}else{if(t instanceof e)return o.fromSourceMap(t).toString();if(t instanceof o)return t.toString();if(this.isMap(t))return JSON.stringify(t);throw new Error("Unsupported previous source map format: "+t.toString())}}else{if(this.inline)return this.decodeInline(this.annotation);if(this.annotation){let l=this.annotation;return h&&(l=f(s(h),l)),this.loadFile(l)}}}startWith(h,t){return h?h.substr(0,t.length)===t:!1}withContent(){return!!(this.consumer().sourcesContent&&this.consumer().sourcesContent.length>0)}}return ct=m,m.default=m,ct}var pt,zr;function Ce(){if(zr)return pt;zr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{fileURLToPath:p,pathToFileURL:d}=B,{isAbsolute:s,resolve:f}=B,{nanoid:a}=Ps(),m=B,n=Gt(),h=xi(),t=Symbol("fromOffsetCache"),l=!!(e&&o),i=!!(f&&s);class r{constructor(u,g={}){if(u===null||typeof u>"u"||typeof u=="object"&&!u.toString)throw new Error(`PostCSS received ${u} instead of CSS string`);if(this.css=u.toString(),this.css[0]==="\uFEFF"||this.css[0]==="￾"?(this.hasBOM=!0,this.css=this.css.slice(1)):this.hasBOM=!1,g.from&&(!i||/^\w+:\/\//.test(g.from)||s(g.from)?this.file=g.from:this.file=f(g.from)),i&&l){let S=new h(this.css,g);if(S.text){this.map=S;let b=S.consumer().file;!this.file&&b&&(this.file=this.mapResolve(b))}}this.file||(this.id=""),this.map&&(this.map.file=this.from)}error(u,g,S,b={}){let v,w,y;if(g&&typeof g=="object"){let R=g,P=S;if(typeof R.offset=="number"){let A=this.fromOffset(R.offset);g=A.line,S=A.col}else g=R.line,S=R.column;if(typeof P.offset=="number"){let A=this.fromOffset(P.offset);w=A.line,y=A.col}else w=P.line,y=P.column}else if(!S){let R=this.fromOffset(g);g=R.line,S=R.col}let x=this.origin(g,S,w,y);return x?v=new n(u,x.endLine===void 0?x.line:{column:x.column,line:x.line},x.endLine===void 0?x.column:{column:x.endColumn,line:x.endLine},x.source,x.file,b.plugin):v=new n(u,w===void 0?g:{column:S,line:g},w===void 0?S:{column:y,line:w},this.css,this.file,b.plugin),v.input={column:S,endColumn:y,endLine:w,line:g,source:this.css},this.file&&(d&&(v.input.url=d(this.file).toString()),v.input.file=this.file),v}fromOffset(u){let g,S;if(this[t])S=this[t];else{let v=this.css.split(` -`);S=new Array(v.length);let w=0;for(let y=0,x=v.length;y=g)b=S.length-1;else{let v=S.length-2,w;for(;b>1),u=S[w+1])b=w+1;else{b=w;break}}return{col:u-S[b]+1,line:b+1}}mapResolve(u){return/^\w+:\/\//.test(u)?u:f(this.map.consumer().sourceRoot||this.map.root||".",u)}origin(u,g,S,b){if(!this.map)return!1;let v=this.map.consumer(),w=v.originalPositionFor({column:g,line:u});if(!w.source)return!1;let y;typeof S=="number"&&(y=v.originalPositionFor({column:b,line:S}));let x;s(w.source)?x=d(w.source):x=new URL(w.source,this.map.consumer().sourceRoot||d(this.map.mapFile));let R={column:w.column,endColumn:y&&y.column,endLine:y&&y.line,line:w.line,url:x.toString()};if(x.protocol==="file:")if(p)R.file=p(x);else throw new Error("file: protocol is not available in this PostCSS build");let P=v.sourceContentFor(w.source);return P&&(R.source=P),R}toJSON(){let u={};for(let g of["hasBOM","css","file","id"])this[g]!=null&&(u[g]=this[g]);return this.map&&(u.map={...this.map},u.map.consumerCache&&(u.map.consumerCache=void 0)),u}get from(){return this.file||this.id}}return pt=r,r.default=r,m&&m.registerInput&&m.registerInput(r),pt}var dt,jr;function Si(){if(jr)return dt;jr=1;let{SourceMapConsumer:e,SourceMapGenerator:o}=B,{dirname:p,relative:d,resolve:s,sep:f}=B,{pathToFileURL:a}=B,m=Ce(),n=!!(e&&o),h=!!(p&&s&&d&&f);class t{constructor(i,r,c,u){this.stringify=i,this.mapOpts=c.map||{},this.root=r,this.opts=c,this.css=u,this.originalCSS=u,this.usesFileUrls=!this.mapOpts.from&&this.mapOpts.absolute,this.memoizedFileURLs=new Map,this.memoizedPaths=new Map,this.memoizedURLs=new Map}addAnnotation(){let i;this.isInline()?i="data:application/json;base64,"+this.toBase64(this.map.toString()):typeof this.mapOpts.annotation=="string"?i=this.mapOpts.annotation:typeof this.mapOpts.annotation=="function"?i=this.mapOpts.annotation(this.opts.to,this.root):i=this.outputFile()+".map";let r=` -`;this.css.includes(`\r -`)&&(r=`\r -`),this.css+=r+"/*# sourceMappingURL="+i+" */"}applyPrevMaps(){for(let i of this.previous()){let r=this.toUrl(this.path(i.file)),c=i.root||p(i.file),u;this.mapOpts.sourcesContent===!1?(u=new e(i.text),u.sourcesContent&&(u.sourcesContent=null)):u=i.consumer(),this.map.applySourceMap(u,r,this.toUrl(this.path(c)))}}clearAnnotation(){if(this.mapOpts.annotation!==!1)if(this.root){let i;for(let r=this.root.nodes.length-1;r>=0;r--)i=this.root.nodes[r],i.type==="comment"&&i.text.indexOf("# sourceMappingURL=")===0&&this.root.removeChild(r)}else this.css&&(this.css=this.css.replace(/\n*?\/\*#[\S\s]*?\*\/$/gm,""))}generate(){if(this.clearAnnotation(),h&&n&&this.isMap())return this.generateMap();{let i="";return this.stringify(this.root,r=>{i+=r}),[i]}}generateMap(){if(this.root)this.generateString();else if(this.previous().length===1){let i=this.previous()[0].consumer();i.file=this.outputFile(),this.map=o.fromSourceMap(i,{ignoreInvalidMapping:!0})}else this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0}),this.map.addMapping({generated:{column:0,line:1},original:{column:0,line:1},source:this.opts.from?this.toUrl(this.path(this.opts.from)):""});return this.isSourcesContent()&&this.setSourcesContent(),this.root&&this.previous().length>0&&this.applyPrevMaps(),this.isAnnotation()&&this.addAnnotation(),this.isInline()?[this.css]:[this.css,this.map]}generateString(){this.css="",this.map=new o({file:this.outputFile(),ignoreInvalidMapping:!0});let i=1,r=1,c="",u={generated:{column:0,line:0},original:{column:0,line:0},source:""},g,S;this.stringify(this.root,(b,v,w)=>{if(this.css+=b,v&&w!=="end"&&(u.generated.line=i,u.generated.column=r-1,v.source&&v.source.start?(u.source=this.sourcePath(v),u.original.line=v.source.start.line,u.original.column=v.source.start.column-1,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,this.map.addMapping(u))),g=b.match(/\n/g),g?(i+=g.length,S=b.lastIndexOf(` -`),r=b.length-S):r+=b.length,v&&w!=="start"){let y=v.parent||{raws:{}};(!(v.type==="decl"||v.type==="atrule"&&!v.nodes)||v!==y.last||y.raws.semicolon)&&(v.source&&v.source.end?(u.source=this.sourcePath(v),u.original.line=v.source.end.line,u.original.column=v.source.end.column-1,u.generated.line=i,u.generated.column=r-2,this.map.addMapping(u)):(u.source=c,u.original.line=1,u.original.column=0,u.generated.line=i,u.generated.column=r-1,this.map.addMapping(u)))}})}isAnnotation(){return this.isInline()?!0:typeof this.mapOpts.annotation<"u"?this.mapOpts.annotation:this.previous().length?this.previous().some(i=>i.annotation):!0}isInline(){if(typeof this.mapOpts.inline<"u")return this.mapOpts.inline;let i=this.mapOpts.annotation;return typeof i<"u"&&i!==!0?!1:this.previous().length?this.previous().some(r=>r.inline):!0}isMap(){return typeof this.opts.map<"u"?!!this.opts.map:this.previous().length>0}isSourcesContent(){return typeof this.mapOpts.sourcesContent<"u"?this.mapOpts.sourcesContent:this.previous().length?this.previous().some(i=>i.withContent()):!0}outputFile(){return this.opts.to?this.path(this.opts.to):this.opts.from?this.path(this.opts.from):"to.css"}path(i){if(this.mapOpts.absolute||i.charCodeAt(0)===60||/^\w+:\/\//.test(i))return i;let r=this.memoizedPaths.get(i);if(r)return r;let c=this.opts.to?p(this.opts.to):".";typeof this.mapOpts.annotation=="string"&&(c=p(s(c,this.mapOpts.annotation)));let u=d(c,i);return this.memoizedPaths.set(i,u),u}previous(){if(!this.previousMaps)if(this.previousMaps=[],this.root)this.root.walk(i=>{if(i.source&&i.source.input.map){let r=i.source.input.map;this.previousMaps.includes(r)||this.previousMaps.push(r)}});else{let i=new m(this.originalCSS,this.opts);i.map&&this.previousMaps.push(i.map)}return this.previousMaps}setSourcesContent(){let i={};if(this.root)this.root.walk(r=>{if(r.source){let c=r.source.input.from;if(c&&!i[c]){i[c]=!0;let u=this.usesFileUrls?this.toFileUrl(c):this.toUrl(this.path(c));this.map.setSourceContent(u,r.source.input.css)}}});else if(this.css){let r=this.opts.from?this.toUrl(this.path(this.opts.from)):"";this.map.setSourceContent(r,this.css)}}sourcePath(i){return this.mapOpts.from?this.toUrl(this.mapOpts.from):this.usesFileUrls?this.toFileUrl(i.source.input.from):this.toUrl(this.path(i.source.input.from))}toBase64(i){return Buffer?Buffer.from(i).toString("base64"):window.btoa(unescape(encodeURIComponent(i)))}toFileUrl(i){let r=this.memoizedFileURLs.get(i);if(r)return r;if(a){let c=a(i).toString();return this.memoizedFileURLs.set(i,c),c}else throw new Error("`map.absolute` option is not available in this PostCSS build")}toUrl(i){let r=this.memoizedURLs.get(i);if(r)return r;f==="\\"&&(i=i.replace(/\\/g,"/"));let c=encodeURI(i).replace(/[#?]/g,encodeURIComponent);return this.memoizedURLs.set(i,c),c}}return dt=t,dt}var mt,Wr;function Ee(){if(Wr)return mt;Wr=1;let e=Re();class o extends e{constructor(d){super(d),this.type="comment"}}return mt=o,o.default=o,mt}var gt,Hr;function Y(){if(Hr)return gt;Hr=1;let{isClean:e,my:o}=Jt(),p=Oe(),d=Ee(),s=Re(),f,a,m,n;function h(i){return i.map(r=>(r.nodes&&(r.nodes=h(r.nodes)),delete r.source,r))}function t(i){if(i[e]=!1,i.proxyOf.nodes)for(let r of i.proxyOf.nodes)t(r)}class l extends s{append(...r){for(let c of r){let u=this.normalize(c,this.last);for(let g of u)this.proxyOf.nodes.push(g)}return this.markDirty(),this}cleanRaws(r){if(super.cleanRaws(r),this.nodes)for(let c of this.nodes)c.cleanRaws(r)}each(r){if(!this.proxyOf.nodes)return;let c=this.getIterator(),u,g;for(;this.indexes[c]r[c](...u.map(g=>typeof g=="function"?(S,b)=>g(S.toProxy(),b):g)):c==="every"||c==="some"?u=>r[c]((g,...S)=>u(g.toProxy(),...S)):c==="root"?()=>r.root().toProxy():c==="nodes"?r.nodes.map(u=>u.toProxy()):c==="first"||c==="last"?r[c].toProxy():r[c]:r[c]},set(r,c,u){return r[c]===u||(r[c]=u,(c==="name"||c==="params"||c==="selector")&&r.markDirty()),!0}}}index(r){return typeof r=="number"?r:(r.proxyOf&&(r=r.proxyOf),this.proxyOf.nodes.indexOf(r))}insertAfter(r,c){let u=this.index(r),g=this.normalize(c,this.proxyOf.nodes[u]).reverse();u=this.index(r);for(let b of g)this.proxyOf.nodes.splice(u+1,0,b);let S;for(let b in this.indexes)S=this.indexes[b],u"u")r=[];else if(Array.isArray(r)){r=r.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type==="root"&&this.type!=="document"){r=r.nodes.slice(0);for(let g of r)g.parent&&g.parent.removeChild(g,"ignore")}else if(r.type)r=[r];else if(r.prop){if(typeof r.value>"u")throw new Error("Value field is missed in node creation");typeof r.value!="string"&&(r.value=String(r.value)),r=[new p(r)]}else if(r.selector)r=[new a(r)];else if(r.name)r=[new m(r)];else if(r.text)r=[new d(r)];else throw new Error("Unknown node type in node creation");return r.map(g=>(g[o]||l.rebuild(g),g=g.proxyOf,g.parent&&g.parent.removeChild(g),g[e]&&t(g),typeof g.raws.before>"u"&&c&&typeof c.raws.before<"u"&&(g.raws.before=c.raws.before.replace(/\S/g,"")),g.parent=this.proxyOf,g))}prepend(...r){r=r.reverse();for(let c of r){let u=this.normalize(c,this.first,"prepend").reverse();for(let g of u)this.proxyOf.nodes.unshift(g);for(let g in this.indexes)this.indexes[g]=this.indexes[g]+u.length}return this.markDirty(),this}push(r){return r.parent=this,this.proxyOf.nodes.push(r),this}removeAll(){for(let r of this.proxyOf.nodes)r.parent=void 0;return this.proxyOf.nodes=[],this.markDirty(),this}removeChild(r){r=this.index(r),this.proxyOf.nodes[r].parent=void 0,this.proxyOf.nodes.splice(r,1);let c;for(let u in this.indexes)c=this.indexes[u],c>=r&&(this.indexes[u]=c-1);return this.markDirty(),this}replaceValues(r,c,u){return u||(u=c,c={}),this.walkDecls(g=>{c.props&&!c.props.includes(g.prop)||c.fast&&!g.value.includes(c.fast)||(g.value=g.value.replace(r,u))}),this.markDirty(),this}some(r){return this.nodes.some(r)}walk(r){return this.each((c,u)=>{let g;try{g=r(c,u)}catch(S){throw c.addToError(S)}return g!==!1&&c.walk&&(g=c.walk(r)),g})}walkAtRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="atrule"&&r.test(u.name))return c(u,g)}):this.walk((u,g)=>{if(u.type==="atrule"&&u.name===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="atrule")return c(u,g)}))}walkComments(r){return this.walk((c,u)=>{if(c.type==="comment")return r(c,u)})}walkDecls(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="decl"&&r.test(u.prop))return c(u,g)}):this.walk((u,g)=>{if(u.type==="decl"&&u.prop===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="decl")return c(u,g)}))}walkRules(r,c){return c?r instanceof RegExp?this.walk((u,g)=>{if(u.type==="rule"&&r.test(u.selector))return c(u,g)}):this.walk((u,g)=>{if(u.type==="rule"&&u.selector===r)return c(u,g)}):(c=r,this.walk((u,g)=>{if(u.type==="rule")return c(u,g)}))}get first(){if(this.proxyOf.nodes)return this.proxyOf.nodes[0]}get last(){if(this.proxyOf.nodes)return this.proxyOf.nodes[this.proxyOf.nodes.length-1]}}return l.registerParse=i=>{f=i},l.registerRule=i=>{a=i},l.registerAtRule=i=>{m=i},l.registerRoot=i=>{n=i},gt=l,l.default=l,l.rebuild=i=>{i.type==="atrule"?Object.setPrototypeOf(i,m.prototype):i.type==="rule"?Object.setPrototypeOf(i,a.prototype):i.type==="decl"?Object.setPrototypeOf(i,p.prototype):i.type==="comment"?Object.setPrototypeOf(i,d.prototype):i.type==="root"&&Object.setPrototypeOf(i,n.prototype),i[o]=!0,i.nodes&&i.nodes.forEach(r=>{l.rebuild(r)})},gt}var yt,Gr;function Vt(){if(Gr)return yt;Gr=1;let e=Y(),o,p;class d extends e{constructor(f){super({type:"document",...f}),this.nodes||(this.nodes=[])}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},yt=d,d.default=d,yt}var wt,Jr;function Ri(){if(Jr)return wt;Jr=1;class e{constructor(p,d={}){if(this.type="warning",this.text=p,d.node&&d.node.source){let s=d.node.rangeBy(d);this.line=s.start.line,this.column=s.start.column,this.endLine=s.end.line,this.endColumn=s.end.column}for(let s in d)this[s]=d[s]}toString(){return this.node?this.node.error(this.text,{index:this.index,plugin:this.plugin,word:this.word}).message:this.plugin?this.plugin+": "+this.text:this.text}}return wt=e,e.default=e,wt}var bt,Vr;function Kt(){if(Vr)return bt;Vr=1;let e=Ri();class o{constructor(d,s,f){this.processor=d,this.messages=[],this.root=s,this.opts=f,this.css=void 0,this.map=void 0}toString(){return this.css}warn(d,s={}){s.plugin||this.lastPlugin&&this.lastPlugin.postcssPlugin&&(s.plugin=this.lastPlugin.postcssPlugin);let f=new e(d,s);return this.messages.push(f),f}warnings(){return this.messages.filter(d=>d.type==="warning")}get content(){return this.css}}return bt=o,o.default=o,bt}var vt,Kr;function As(){if(Kr)return vt;Kr=1;const e=39,o=34,p=92,d=47,s=10,f=32,a=12,m=9,n=13,h=91,t=93,l=40,i=41,r=123,c=125,u=59,g=42,S=58,b=64,v=/[\t\n\f\r "#'()/;[\\\]{}]/g,w=/[\t\n\f\r !"#'():;@[\\\]{}]|\/(?=\*)/g,y=/.[\r\n"'(/\\]/,x=/[\da-f]/i;return vt=function(P,A={}){let E=P.css.valueOf(),T=A.ignoreErrors,N,O,te,G,z,L,U,re,$,M,ie=E.length,C=0,J=[],j=[];function Pe(){return C}function V(D){throw P.error("Unclosed "+D,C)}function Ae(){return j.length===0&&C>=ie}function Ne(D){if(j.length)return j.pop();if(C>=ie)return;let K=D?D.ignoreUnclosed:!1;switch(N=E.charCodeAt(C),N){case s:case f:case m:case n:case a:{O=C;do O+=1,N=E.charCodeAt(O);while(N===f||N===s||N===m||N===n||N===a);M=["space",E.slice(C,O)],C=O-1;break}case h:case t:case r:case c:case S:case u:case i:{let se=String.fromCharCode(N);M=[se,se,C];break}case l:{if(re=J.length?J.pop()[1]:"",$=E.charCodeAt(C+1),re==="url"&&$!==e&&$!==o&&$!==f&&$!==s&&$!==m&&$!==a&&$!==n){O=C;do{if(L=!1,O=E.indexOf(")",O+1),O===-1)if(T||K){O=C;break}else V("bracket");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["brackets",E.slice(C,O+1),C,O],C=O}else O=E.indexOf(")",C+1),G=E.slice(C,O+1),O===-1||y.test(G)?M=["(","(",C]:(M=["brackets",G,C,O],C=O);break}case e:case o:{te=N===e?"'":'"',O=C;do{if(L=!1,O=E.indexOf(te,O+1),O===-1)if(T||K){O=C+1;break}else V("string");for(U=O;E.charCodeAt(U-1)===p;)U-=1,L=!L}while(L);M=["string",E.slice(C,O+1),C,O],C=O;break}case b:{v.lastIndex=C+1,v.test(E),v.lastIndex===0?O=E.length-1:O=v.lastIndex-2,M=["at-word",E.slice(C,O+1),C,O],C=O;break}case p:{for(O=C,z=!0;E.charCodeAt(O+1)===p;)O+=1,z=!z;if(N=E.charCodeAt(O+1),z&&N!==d&&N!==f&&N!==s&&N!==m&&N!==n&&N!==a&&(O+=1,x.test(E.charAt(O)))){for(;x.test(E.charAt(O+1));)O+=1;E.charCodeAt(O+1)===f&&(O+=1)}M=["word",E.slice(C,O+1),C,O],C=O;break}default:{N===d&&E.charCodeAt(C+1)===g?(O=E.indexOf("*/",C+2)+1,O===0&&(T||K?O=E.length:V("comment")),M=["comment",E.slice(C,O+1),C,O],C=O):(w.lastIndex=C+1,w.test(E),w.lastIndex===0?O=E.length-1:O=w.lastIndex-2,M=["word",E.slice(C,O+1),C,O],J.push(M),C=O);break}}return C++,M}function Me(D){j.push(D)}return{back:Me,endOfFile:Ae,nextToken:Ne,position:Pe}},vt}var xt,Qr;function Qt(){if(Qr)return xt;Qr=1;let e=Y();class o extends e{constructor(d){super(d),this.type="atrule"}append(...d){return this.proxyOf.nodes||(this.nodes=[]),super.append(...d)}prepend(...d){return this.proxyOf.nodes||(this.nodes=[]),super.prepend(...d)}}return xt=o,o.default=o,e.registerAtRule(o),xt}var St,Yr;function oe(){if(Yr)return St;Yr=1;let e=Y(),o,p;class d extends e{constructor(f){super(f),this.type="root",this.nodes||(this.nodes=[])}normalize(f,a,m){let n=super.normalize(f);if(a){if(m==="prepend")this.nodes.length>1?a.raws.before=this.nodes[1].raws.before:delete a.raws.before;else if(this.first!==a)for(let h of n)h.raws.before=a.raws.before}return n}removeChild(f,a){let m=this.index(f);return!a&&m===0&&this.nodes.length>1&&(this.nodes[1].raws.before=this.nodes[m].raws.before),super.removeChild(f)}toResult(f={}){return new o(new p,this,f).stringify()}}return d.registerLazyResult=s=>{o=s},d.registerProcessor=s=>{p=s},St=d,d.default=d,e.registerRoot(d),St}var Rt,Xr;function Oi(){if(Xr)return Rt;Xr=1;let e={comma(o){return e.split(o,[","],!0)},space(o){let p=[" ",` -`," "];return e.split(o,p)},split(o,p,d){let s=[],f="",a=!1,m=0,n=!1,h="",t=!1;for(let l of o)t?t=!1:l==="\\"?t=!0:n?l===h&&(n=!1):l==='"'||l==="'"?(n=!0,h=l):l==="("?m+=1:l===")"?m>0&&(m-=1):m===0&&p.includes(l)&&(a=!0),a?(f!==""&&s.push(f.trim()),f="",a=!1):f+=l;return(d||f!=="")&&s.push(f.trim()),s}};return Rt=e,e.default=e,Rt}var Ot,Zr;function Yt(){if(Zr)return Ot;Zr=1;let e=Y(),o=Oi();class p extends e{constructor(s){super(s),this.type="rule",this.nodes||(this.nodes=[])}get selectors(){return o.comma(this.selector)}set selectors(s){let f=this.selector?this.selector.match(/,\s*/):null,a=f?f[0]:","+this.raw("between","beforeOpen");this.selector=s.join(a)}}return Ot=p,p.default=p,e.registerRule(p),Ot}var Ct,ei;function Ns(){if(ei)return Ct;ei=1;let e=Oe(),o=As(),p=Ee(),d=Qt(),s=oe(),f=Yt();const a={empty:!0,space:!0};function m(h){for(let t=h.length-1;t>=0;t--){let l=h[t],i=l[3]||l[2];if(i)return i}}class n{constructor(t){this.input=t,this.root=new s,this.current=this.root,this.spaces="",this.semicolon=!1,this.createTokenizer(),this.root.source={input:t,start:{column:1,line:1,offset:0}}}atrule(t){let l=new d;l.name=t[1].slice(1),l.name===""&&this.unnamedAtrule(l,t),this.init(l,t[2]);let i,r,c,u=!1,g=!1,S=[],b=[];for(;!this.tokenizer.endOfFile();){if(t=this.tokenizer.nextToken(),i=t[0],i==="("||i==="["?b.push(i==="("?")":"]"):i==="{"&&b.length>0?b.push("}"):i===b[b.length-1]&&b.pop(),b.length===0)if(i===";"){l.source.end=this.getPosition(t[2]),l.source.end.offset++,this.semicolon=!0;break}else if(i==="{"){g=!0;break}else if(i==="}"){if(S.length>0){for(c=S.length-1,r=S[c];r&&r[0]==="space";)r=S[--c];r&&(l.source.end=this.getPosition(r[3]||r[2]),l.source.end.offset++)}this.end(t);break}else S.push(t);else S.push(t);if(this.tokenizer.endOfFile()){u=!0;break}}l.raws.between=this.spacesAndCommentsFromEnd(S),S.length?(l.raws.afterName=this.spacesAndCommentsFromStart(S),this.raw(l,"params",S),u&&(t=S[S.length-1],l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++,this.spaces=l.raws.between,l.raws.between="")):(l.raws.afterName="",l.params=""),g&&(l.nodes=[],this.current=l)}checkMissedSemicolon(t){let l=this.colon(t);if(l===!1)return;let i=0,r;for(let c=l-1;c>=0&&(r=t[c],!(r[0]!=="space"&&(i+=1,i===2)));c--);throw this.input.error("Missed semicolon",r[0]==="word"?r[3]+1:r[2])}colon(t){let l=0,i,r,c;for(let[u,g]of t.entries()){if(i=g,r=i[0],r==="("&&(l+=1),r===")"&&(l-=1),l===0&&r===":")if(!c)this.doubleColon(i);else{if(c[0]==="word"&&c[1]==="progid")continue;return u}c=i}return!1}comment(t){let l=new p;this.init(l,t[2]),l.source.end=this.getPosition(t[3]||t[2]),l.source.end.offset++;let i=t[1].slice(2,-2);if(/^\s*$/.test(i))l.text="",l.raws.left=i,l.raws.right="";else{let r=i.match(/^(\s*)([^]*\S)(\s*)$/);l.text=r[2],l.raws.left=r[1],l.raws.right=r[3]}}createTokenizer(){this.tokenizer=o(this.input)}decl(t,l){let i=new e;this.init(i,t[0][2]);let r=t[t.length-1];for(r[0]===";"&&(this.semicolon=!0,t.pop()),i.source.end=this.getPosition(r[3]||r[2]||m(t)),i.source.end.offset++;t[0][0]!=="word";)t.length===1&&this.unknownWord(t),i.raws.before+=t.shift()[1];for(i.source.start=this.getPosition(t[0][2]),i.prop="";t.length;){let b=t[0][0];if(b===":"||b==="space"||b==="comment")break;i.prop+=t.shift()[1]}i.raws.between="";let c;for(;t.length;)if(c=t.shift(),c[0]===":"){i.raws.between+=c[1];break}else c[0]==="word"&&/\w/.test(c[1])&&this.unknownWord([c]),i.raws.between+=c[1];(i.prop[0]==="_"||i.prop[0]==="*")&&(i.raws.before+=i.prop[0],i.prop=i.prop.slice(1));let u=[],g;for(;t.length&&(g=t[0][0],!(g!=="space"&&g!=="comment"));)u.push(t.shift());this.precheckMissedSemicolon(t);for(let b=t.length-1;b>=0;b--){if(c=t[b],c[1].toLowerCase()==="!important"){i.important=!0;let v=this.stringFrom(t,b);v=this.spacesFromEnd(t)+v,v!==" !important"&&(i.raws.important=v);break}else if(c[1].toLowerCase()==="important"){let v=t.slice(0),w="";for(let y=b;y>0;y--){let x=v[y][0];if(w.trim().indexOf("!")===0&&x!=="space")break;w=v.pop()[1]+w}w.trim().indexOf("!")===0&&(i.important=!0,i.raws.important=w,t=v)}if(c[0]!=="space"&&c[0]!=="comment")break}t.some(b=>b[0]!=="space"&&b[0]!=="comment")&&(i.raws.between+=u.map(b=>b[1]).join(""),u=[]),this.raw(i,"value",u.concat(t),l),i.value.includes(":")&&!l&&this.checkMissedSemicolon(t)}doubleColon(t){throw this.input.error("Double colon",{offset:t[2]},{offset:t[2]+t[1].length})}emptyRule(t){let l=new f;this.init(l,t[2]),l.selector="",l.raws.between="",this.current=l}end(t){this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.semicolon=!1,this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.spaces="",this.current.parent?(this.current.source.end=this.getPosition(t[2]),this.current.source.end.offset++,this.current=this.current.parent):this.unexpectedClose(t)}endFile(){this.current.parent&&this.unclosedBlock(),this.current.nodes&&this.current.nodes.length&&(this.current.raws.semicolon=this.semicolon),this.current.raws.after=(this.current.raws.after||"")+this.spaces,this.root.source.end=this.getPosition(this.tokenizer.position())}freeSemicolon(t){if(this.spaces+=t[1],this.current.nodes){let l=this.current.nodes[this.current.nodes.length-1];l&&l.type==="rule"&&!l.raws.ownSemicolon&&(l.raws.ownSemicolon=this.spaces,this.spaces="")}}getPosition(t){let l=this.input.fromOffset(t);return{column:l.col,line:l.line,offset:t}}init(t,l){this.current.push(t),t.source={input:this.input,start:this.getPosition(l)},t.raws.before=this.spaces,this.spaces="",t.type!=="comment"&&(this.semicolon=!1)}other(t){let l=!1,i=null,r=!1,c=null,u=[],g=t[1].startsWith("--"),S=[],b=t;for(;b;){if(i=b[0],S.push(b),i==="("||i==="[")c||(c=b),u.push(i==="("?")":"]");else if(g&&r&&i==="{")c||(c=b),u.push("}");else if(u.length===0)if(i===";")if(r){this.decl(S,g);return}else break;else if(i==="{"){this.rule(S);return}else if(i==="}"){this.tokenizer.back(S.pop()),l=!0;break}else i===":"&&(r=!0);else i===u[u.length-1]&&(u.pop(),u.length===0&&(c=null));b=this.tokenizer.nextToken()}if(this.tokenizer.endOfFile()&&(l=!0),u.length>0&&this.unclosedBracket(c),l&&r){if(!g)for(;S.length&&(b=S[S.length-1][0],!(b!=="space"&&b!=="comment"));)this.tokenizer.back(S.pop());this.decl(S,g)}else this.unknownWord(S)}parse(){let t;for(;!this.tokenizer.endOfFile();)switch(t=this.tokenizer.nextToken(),t[0]){case"space":this.spaces+=t[1];break;case";":this.freeSemicolon(t);break;case"}":this.end(t);break;case"comment":this.comment(t);break;case"at-word":this.atrule(t);break;case"{":this.emptyRule(t);break;default:this.other(t);break}this.endFile()}precheckMissedSemicolon(){}raw(t,l,i,r){let c,u,g=i.length,S="",b=!0,v,w;for(let y=0;yx+R[1],"");t.raws[l]={raw:y,value:S}}t[l]=S}rule(t){t.pop();let l=new f;this.init(l,t[0][2]),l.raws.between=this.spacesAndCommentsFromEnd(t),this.raw(l,"selector",t),this.current=l}spacesAndCommentsFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],!(l!=="space"&&l!=="comment"));)i=t.pop()[1]+i;return i}spacesAndCommentsFromStart(t){let l,i="";for(;t.length&&(l=t[0][0],!(l!=="space"&&l!=="comment"));)i+=t.shift()[1];return i}spacesFromEnd(t){let l,i="";for(;t.length&&(l=t[t.length-1][0],l==="space");)i=t.pop()[1]+i;return i}stringFrom(t,l){let i="";for(let r=l;rg(w)),v}let S={};class b{constructor(w,y,x){this.stringified=!1,this.processed=!1;let R;if(typeof y=="object"&&y!==null&&(y.type==="root"||y.type==="document"))R=g(y);else if(y instanceof b||y instanceof a)R=g(y.root),y.map&&(typeof x.map>"u"&&(x.map={}),x.map.inline||(x.map.inline=!1),x.map.prev=y.map);else{let P=m;x.syntax&&(P=x.syntax.parse),x.parser&&(P=x.parser),P.parse&&(P=P.parse);try{R=P(y,x)}catch(A){this.processed=!0,this.error=A}R&&!R[o]&&s.rebuild(R)}this.result=new a(w,R,x),this.helpers={...S,postcss:S,result:this.result},this.plugins=this.processor.plugins.map(P=>typeof P=="object"&&P.prepare?{...P,...P.prepare(this.result)}:P)}async(){return this.error?Promise.reject(this.error):this.processed?Promise.resolve(this.result):(this.processing||(this.processing=this.runAsync()),this.processing)}catch(w){return this.async().catch(w)}finally(w){return this.async().then(w,w)}getAsyncError(){throw new Error("Use process(css).then(cb) to work with async plugins")}handleError(w,y){let x=this.result.lastPlugin;try{y&&y.addToError(w),this.error=w,w.name==="CssSyntaxError"&&!w.plugin?(w.plugin=x.postcssPlugin,w.setMessage()):x.postcssVersion}catch(R){console&&console.error&&console.error(R)}return w}prepareVisitors(){this.listeners={};let w=(y,x,R)=>{this.listeners[x]||(this.listeners[x]=[]),this.listeners[x].push([y,R])};for(let y of this.plugins)if(typeof y=="object")for(let x in y){if(!t[x]&&/^[A-Z]/.test(x))throw new Error(`Unknown event ${x} in ${y.postcssPlugin}. Try to update PostCSS (${this.processor.version} now).`);if(!l[x])if(typeof y[x]=="object")for(let R in y[x])R==="*"?w(y,x,y[x][R]):w(y,x+"-"+R.toLowerCase(),y[x][R]);else typeof y[x]=="function"&&w(y,x,y[x])}this.hasListener=Object.keys(this.listeners).length>0}async runAsync(){this.plugin=0;for(let w=0;w0;){let x=this.visitTick(y);if(r(x))try{await x}catch(R){let P=y[y.length-1].node;throw this.handleError(R,P)}}}if(this.listeners.OnceExit)for(let[y,x]of this.listeners.OnceExit){this.result.lastPlugin=y;try{if(w.type==="document"){let R=w.nodes.map(P=>x(P,this.helpers));await Promise.all(R)}else await x(w,this.helpers)}catch(R){throw this.handleError(R)}}}return this.processed=!0,this.stringify()}runOnRoot(w){this.result.lastPlugin=w;try{if(typeof w=="object"&&w.Once){if(this.result.root.type==="document"){let y=this.result.root.nodes.map(x=>w.Once(x,this.helpers));return r(y[0])?Promise.all(y):y}return w.Once(this.result.root,this.helpers)}else if(typeof w=="function")return w(this.result.root,this.result)}catch(y){throw this.handleError(y)}}stringify(){if(this.error)throw this.error;if(this.stringified)return this.result;this.stringified=!0,this.sync();let w=this.result.opts,y=d;w.syntax&&(y=w.syntax.stringify),w.stringifier&&(y=w.stringifier),y.stringify&&(y=y.stringify);let R=new p(y,this.result.root,this.result.opts).generate();return this.result.css=R[0],this.result.map=R[1],this.result}sync(){if(this.error)throw this.error;if(this.processed)return this.result;if(this.processed=!0,this.processing)throw this.getAsyncError();for(let w of this.plugins){let y=this.runOnRoot(w);if(r(y))throw this.getAsyncError()}if(this.prepareVisitors(),this.hasListener){let w=this.result.root;for(;!w[e];)w[e]=!0,this.walkSync(w);if(this.listeners.OnceExit)if(w.type==="document")for(let y of w.nodes)this.visitSync(this.listeners.OnceExit,y);else this.visitSync(this.listeners.OnceExit,w)}return this.result}then(w,y){return this.async().then(w,y)}toString(){return this.css}visitSync(w,y){for(let[x,R]of w){this.result.lastPlugin=x;let P;try{P=R(y,this.helpers)}catch(A){throw this.handleError(A,y.proxyOf)}if(y.type!=="root"&&y.type!=="document"&&!y.parent)return!0;if(r(P))throw this.getAsyncError()}}visitTick(w){let y=w[w.length-1],{node:x,visitors:R}=y;if(x.type!=="root"&&x.type!=="document"&&!x.parent){w.pop();return}if(R.length>0&&y.visitorIndex{R[e]||this.walkSync(R)});else{let R=this.listeners[x];if(R&&this.visitSync(R,w.toProxy()))return}}warnings(){return this.sync().warnings()}get content(){return this.stringify().content}get css(){return this.stringify().css}get map(){return this.stringify().map}get messages(){return this.sync().messages}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){return this.sync().root}get[Symbol.toStringTag](){return"LazyResult"}}return b.registerPostcss=v=>{S=v},Pt=b,b.default=b,n.registerLazyResult(b),f.registerLazyResult(b),Pt}var At,ii;function Ms(){if(ii)return At;ii=1;let e=Si(),o=Se(),p=Xt();const d=Kt();class s{constructor(a,m,n){m=m.toString(),this.stringified=!1,this._processor=a,this._css=m,this._opts=n,this._map=void 0;let h,t=o;this.result=new d(this._processor,h,this._opts),this.result.css=m;let l=this;Object.defineProperty(this.result,"root",{get(){return l.root}});let i=new e(t,h,this._opts,m);if(i.isMap()){let[r,c]=i.generate();r&&(this.result.css=r),c&&(this.result.map=c)}else i.clearAnnotation(),this.result.css=i.css}async(){return this.error?Promise.reject(this.error):Promise.resolve(this.result)}catch(a){return this.async().catch(a)}finally(a){return this.async().then(a,a)}sync(){if(this.error)throw this.error;return this.result}then(a,m){return this.async().then(a,m)}toString(){return this._css}warnings(){return[]}get content(){return this.result.css}get css(){return this.result.css}get map(){return this.result.map}get messages(){return[]}get opts(){return this.result.opts}get processor(){return this.result.processor}get root(){if(this._root)return this._root;let a,m=p;try{a=m(this._css,this._opts)}catch(n){this.error=n}if(this.error)throw this.error;return this._root=a,a}get[Symbol.toStringTag](){return"NoWorkResult"}}return At=s,s.default=s,At}var Nt,si;function _s(){if(si)return Nt;si=1;let e=Ms(),o=Ci(),p=Vt(),d=oe();class s{constructor(a=[]){this.version="8.4.38",this.plugins=this.normalize(a)}normalize(a){let m=[];for(let n of a)if(n.postcss===!0?n=n():n.postcss&&(n=n.postcss),typeof n=="object"&&Array.isArray(n.plugins))m=m.concat(n.plugins);else if(typeof n=="object"&&n.postcssPlugin)m.push(n);else if(typeof n=="function")m.push(n);else if(!(typeof n=="object"&&(n.parse||n.stringify)))throw new Error(n+" is not a PostCSS plugin");return m}process(a,m={}){return!this.plugins.length&&!m.parser&&!m.stringifier&&!m.syntax?new e(this,a,m):new o(this,a,m)}use(a){return this.plugins=this.plugins.concat(this.normalize([a])),this}}return Nt=s,s.default=s,d.registerProcessor(s),p.registerProcessor(s),Nt}var Mt,ni;function Is(){if(ni)return Mt;ni=1;let e=Oe(),o=xi(),p=Ee(),d=Qt(),s=Ce(),f=oe(),a=Yt();function m(n,h){if(Array.isArray(n))return n.map(i=>m(i));let{inputs:t,...l}=n;if(t){h=[];for(let i of t){let r={...i,__proto__:s.prototype};r.map&&(r.map={...r.map,__proto__:o.prototype}),h.push(r)}}if(l.nodes&&(l.nodes=n.nodes.map(i=>m(i,h))),l.source){let{inputId:i,...r}=l.source;l.source=r,i!=null&&(l.source.input=h[i])}if(l.type==="root")return new f(l);if(l.type==="decl")return new e(l);if(l.type==="rule")return new a(l);if(l.type==="comment")return new p(l);if(l.type==="atrule")return new d(l);throw new Error("Unknown node type: "+n.type)}return Mt=m,m.default=m,Mt}var _t,oi;function Ls(){if(oi)return _t;oi=1;let e=Gt(),o=Oe(),p=Ci(),d=Y(),s=_s(),f=Se(),a=Is(),m=Vt(),n=Ri(),h=Ee(),t=Qt(),l=Kt(),i=Ce(),r=Xt(),c=Oi(),u=Yt(),g=oe(),S=Re();function b(...v){return v.length===1&&Array.isArray(v[0])&&(v=v[0]),new s(v)}return b.plugin=function(w,y){let x=!1;function R(...A){console&&console.warn&&!x&&(x=!0,console.warn(w+`: postcss.plugin was deprecated. Migration guide: -https://evilmartians.com/chronicles/postcss-8-plugin-migration`),me.LANG&&me.LANG.startsWith("cn")&&console.warn(w+`: 里面 postcss.plugin 被弃用. 迁移指南: -https://www.w3ctech.com/topic/2226`));let E=y(...A);return E.postcssPlugin=w,E.postcssVersion=new s().version,E}let P;return Object.defineProperty(R,"postcss",{get(){return P||(P=R()),P}}),R.process=function(A,E,T){return b([R(T)]).process(A,E)},R},b.stringify=f,b.parse=r,b.fromJSON=a,b.list=c,b.comment=v=>new h(v),b.atRule=v=>new t(v),b.decl=v=>new o(v),b.rule=v=>new u(v),b.root=v=>new g(v),b.document=v=>new m(v),b.CssSyntaxError=e,b.Declaration=o,b.Container=d,b.Processor=s,b.Document=m,b.Comment=h,b.Warning=n,b.AtRule=t,b.Result=l,b.Input=i,b.Rule=u,b.Root=g,b.Node=S,p.registerPostcss(b),_t=b,b.default=b,_t}var $s=Ls();const I=Ss($s);I.stringify;I.fromJSON;I.plugin;I.parse;I.list;I.document;I.comment;I.atRule;I.rule;I.decl;I.root;I.CssSyntaxError;I.Declaration;I.Container;I.Processor;I.Document;I.Comment;I.Warning;I.AtRule;I.Result;I.Input;I.Rule;I.Root;I.Node;class Zt{constructor(...o){k(this,"parentElement",null),k(this,"parentNode",null),k(this,"ownerDocument"),k(this,"firstChild",null),k(this,"lastChild",null),k(this,"previousSibling",null),k(this,"nextSibling",null),k(this,"ELEMENT_NODE",1),k(this,"TEXT_NODE",3),k(this,"nodeType"),k(this,"nodeName"),k(this,"RRNodeType")}get childNodes(){const o=[];let p=this.firstChild;for(;p;)o.push(p),p=p.nextSibling;return o}contains(o){if(o instanceof Zt){if(o.ownerDocument!==this.ownerDocument)return!1;if(o===this)return!0}else return!1;for(;o.parentNode;){if(o.parentNode===this)return!0;o=o.parentNode}return!1}appendChild(o){throw new Error("RRDomException: Failed to execute 'appendChild' on 'RRNode': This RRNode type does not support this method.")}insertBefore(o,p){throw new Error("RRDomException: Failed to execute 'insertBefore' on 'RRNode': This RRNode type does not support this method.")}removeChild(o){throw new Error("RRDomException: Failed to execute 'removeChild' on 'RRNode': This RRNode type does not support this method.")}toString(){return"RRNode"}}const li={Node:["childNodes","parentNode","parentElement","textContent"],ShadowRoot:["host","styleSheets"],Element:["shadowRoot","querySelector","querySelectorAll"],MutationObserver:[]},ai={Node:["contains","getRootNode"],ShadowRoot:["getSelection"],Element:[],MutationObserver:["constructor"]},pe={};function Ts(e){var o,p;const d=(p=(o=globalThis?.Zone)==null?void 0:o.__symbol__)==null?void 0:p.call(o,e);if(d&&globalThis[d])return globalThis[d]}function er(e){if(pe[e])return pe[e];const o=Ts(e)||globalThis[e],p=o.prototype,d=e in li?li[e]:void 0,s=!!(d&&d.every(m=>{var n,h;return!!((h=(n=Object.getOwnPropertyDescriptor(p,m))==null?void 0:n.get)!=null&&h.toString().includes("[native code]"))})),f=e in ai?ai[e]:void 0,a=!!(f&&f.every(m=>{var n;return typeof p[m]=="function"&&((n=p[m])==null?void 0:n.toString().includes("[native code]"))}));if(s&&a)return pe[e]=o.prototype,o.prototype;try{const m=document.createElement("iframe");document.body.appendChild(m);const n=m.contentWindow;if(!n)return o.prototype;const h=n[e].prototype;return document.body.removeChild(m),h?pe[e]=h:p}catch{return p}}const It={};function H(e,o,p){var d;const s=`${e}.${String(p)}`;if(It[s])return It[s].call(o);const f=er(e),a=(d=Object.getOwnPropertyDescriptor(f,p))==null?void 0:d.get;return a?(It[s]=a,a.call(o)):o[p]}const Lt={};function Ei(e,o,p){const d=`${e}.${String(p)}`;if(Lt[d])return Lt[d].bind(o);const f=er(e)[p];return typeof f!="function"?o[p]:(Lt[d]=f,f.bind(o))}function Us(e){return H("Node",e,"childNodes")}function Ds(e){return H("Node",e,"parentNode")}function ks(e){return H("Node",e,"parentElement")}function qs(e){return H("Node",e,"textContent")}function Fs(e,o){return Ei("Node",e,"contains")(o)}function Bs(e){return Ei("Node",e,"getRootNode")()}function zs(e){return!e||!("host"in e)?null:H("ShadowRoot",e,"host")}function js(e){return e.styleSheets}function Ws(e){return!e||!("shadowRoot"in e)?null:H("Element",e,"shadowRoot")}function Hs(e,o){return H("Element",e,"querySelector")(o)}function Gs(e,o){return H("Element",e,"querySelectorAll")(o)}function Js(){return er("MutationObserver").constructor}const q={childNodes:Us,parentNode:Ds,parentElement:ks,textContent:qs,contains:Fs,getRootNode:Bs,host:zs,styleSheets:js,shadowRoot:Ws,querySelector:Hs,querySelectorAll:Gs,mutationObserver:Js};function Vs(e,o,p=document){const d={capture:!0,passive:!0};return p.addEventListener(e,o,d),()=>p.removeEventListener(e,o,d)}const ee=`Please stop import mirror directly. Instead of that,\r -now you can use replayer.getMirror() to access the mirror instance of a replayer,\r -or you can use record.mirror to access the mirror instance during recording.`;let Ut={map:{},getId(){return console.error(ee),-1},getNode(){return console.error(ee),null},removeNodeFromMap(){console.error(ee)},has(){return console.error(ee),!1},reset(){console.error(ee)}};typeof window<"u"&&window.Proxy&&window.Reflect&&(Ut=new Proxy(Ut,{get(e,o,p){return o==="map"&&console.error(ee),Reflect.get(e,o,p)}}));function Ks(e,o,p={}){let d=null,s=0;return function(...f){const a=Date.now();!s&&p.leading===!1&&(s=a);const m=o-(a-s),n=this;m<=0||m>o?(d&&(clearTimeout(d),d=null),s=a,e.apply(n,f)):!d&&p.trailing!==!1&&(d=setTimeout(()=>{s=p.leading===!1?0:Date.now(),d=null,e.apply(n,f)},m))}}function Pi(e,o,p,d,s=window){const f=s.Object.getOwnPropertyDescriptor(e,o);return s.Object.defineProperty(e,o,d?p:{set(a){setTimeout(()=>{p.set.call(this,a)},0),f&&f.set&&f.set.call(this,a)}}),()=>Pi(e,o,f||{},!0)}function Qs(e,o,p){try{if(!(o in e))return()=>{};const d=e[o],s=p(d);return typeof s=="function"&&(s.prototype=s.prototype||{},Object.defineProperties(s,{__rrweb_original__:{enumerable:!1,value:d}})),e[o]=s,()=>{e[o]=d}}catch{return()=>{}}}let Ai=Date.now;/[1-9][0-9]{12}/.test(Date.now().toString())||(Ai=()=>new Date().getTime());function Ys(e){var o,p,d,s;const f=e.document;return{left:f.scrollingElement?f.scrollingElement.scrollLeft:e.pageXOffset!==void 0?e.pageXOffset:f.documentElement.scrollLeft||f?.body&&((o=q.parentElement(f.body))==null?void 0:o.scrollLeft)||((p=f?.body)==null?void 0:p.scrollLeft)||0,top:f.scrollingElement?f.scrollingElement.scrollTop:e.pageYOffset!==void 0?e.pageYOffset:f?.documentElement.scrollTop||f?.body&&((d=q.parentElement(f.body))==null?void 0:d.scrollTop)||((s=f?.body)==null?void 0:s.scrollTop)||0}}function Xs(){return window.innerHeight||document.documentElement&&document.documentElement.clientHeight||document.body&&document.body.clientHeight}function Zs(){return window.innerWidth||document.documentElement&&document.documentElement.clientWidth||document.body&&document.body.clientWidth}function Ni(e){return e?e.nodeType===e.ELEMENT_NODE?e:q.parentElement(e):null}function en(e,o,p,d){if(!e)return!1;const s=Ni(e);if(!s)return!1;try{if(typeof o=="string"){if(s.classList.contains(o)||d&&s.closest("."+o)!==null)return!0}else if(Tt(s,o,d))return!0}catch{}return!!(p&&(s.matches(p)||d&&s.closest(p)!==null))}function tn(e,o){return o.getId(e)!==-1}function rn(e,o,p){return e.tagName==="TITLE"&&p.headTitleMutations?!0:o.getId(e)===os}function Mi(e,o){if(is(e))return!1;const p=o.getId(e);if(!o.has(p))return!0;const d=q.parentNode(e);return d&&d.nodeType===e.DOCUMENT_NODE?!1:d?Mi(d,o):!0}function sn(e){return!!e.changedTouches}function nn(e=window){"NodeList"in e&&!e.NodeList.prototype.forEach&&(e.NodeList.prototype.forEach=Array.prototype.forEach),"DOMTokenList"in e&&!e.DOMTokenList.prototype.forEach&&(e.DOMTokenList.prototype.forEach=Array.prototype.forEach)}function on(e){const o={},p=(s,f)=>{const a={value:s,parent:f,children:[]};return o[s.node.id]=a,a},d=[];for(const s of e){const{nextId:f,parentId:a}=s;if(f&&f in o){const m=o[f];if(m.parent){const n=m.parent.children.indexOf(m);m.parent.children.splice(n,0,p(s,m.parent))}else{const n=d.indexOf(m);d.splice(n,0,p(s,null))}continue}if(a in o){const m=o[a];m.children.push(p(s,m));continue}d.push(p(s,null))}return d}function _i(e,o){o(e.value);for(let p=e.children.length-1;p>=0;p--)_i(e.children[p],o)}function ln(e,o){return!!(e.nodeName==="IFRAME"&&o.getMeta(e))}function an(e,o){return!!(e.nodeName==="LINK"&&e.nodeType===e.ELEMENT_NODE&&e.getAttribute&&e.getAttribute("rel")==="stylesheet"&&o.getMeta(e))}function Ii(e,o){var p,d;const s=(d=(p=e.ownerDocument)==null?void 0:p.defaultView)==null?void 0:d.frameElement;if(!s||s===o)return{x:0,y:0,relativeScale:1,absoluteScale:1};const f=s.getBoundingClientRect(),a=Ii(s,o),m=f.height/s.clientHeight;return{x:f.x*a.relativeScale+a.x,y:f.y*a.relativeScale+a.y,relativeScale:m,absoluteScale:a.absoluteScale*m}}function un(e){return e?e instanceof Zt&&"shadowRoot"in e?!!e.shadowRoot:!!q.shadowRoot(e):!1}function Li(e,o){const p=e[o[0]];return o.length===1?p:Li(p.cssRules[o[1]].cssRules,o.slice(2))}function fn(e){const o=[...e],p=o.pop();return{positions:o,index:p}}function hn(e){const o=new Set,p=[];for(let d=e.length;d--;){const s=e[d];o.has(s.id)||(p.push(s),o.add(s.id))}return p}class cn{constructor(){_e(this,"id",1),_e(this,"styleIDMap",new WeakMap),_e(this,"idStyleMap",new Map)}getId(o){return this.styleIDMap.get(o)??-1}has(o){return this.styleIDMap.has(o)}add(o,p){if(this.has(o))return this.getId(o);let d;return p===void 0?d=this.id++:d=p,this.styleIDMap.set(o,d),this.idStyleMap.set(d,o),d}getStyle(o){return this.idStyleMap.get(o)||null}reset(){this.styleIDMap=new WeakMap,this.idStyleMap=new Map,this.id=1}generateId(){return this.id++}}function $i(e){var o;let p=null;return"getRootNode"in e&&((o=q.getRootNode(e))==null?void 0:o.nodeType)===Node.DOCUMENT_FRAGMENT_NODE&&q.host(q.getRootNode(e))&&(p=q.host(q.getRootNode(e))),p}function Ti(e){let o=e,p;for(;p=$i(o);)o=p;return o}function Ui(e){const o=e.ownerDocument;if(!o)return!1;const p=Ti(e);return q.contains(o,p)}function pn(e){const o=e.ownerDocument;return o?q.contains(o,e)||Ui(e):!1}const dn=Object.freeze(Object.defineProperty({__proto__:null,StyleSheetMirror:cn,get _mirror(){return Ut},closestElementOfNode:Ni,getBaseDimension:Ii,getNestedRule:Li,getPositionsAndIndex:fn,getRootShadowHost:Ti,getShadowHost:$i,getWindowHeight:Xs,getWindowScroll:Ys,getWindowWidth:Zs,hasShadowRoot:un,hookSetter:Pi,inDom:pn,isAncestorRemoved:Mi,isBlocked:en,isIgnored:rn,isSerialized:tn,isSerializedIframe:ln,isSerializedStylesheet:an,iterateResolveTree:_i,legacy_isTouchEvent:sn,get nowTimestamp(){return Ai},on:Vs,patch:Qs,polyfill:nn,queueToResolveTrees:on,shadowHostInDom:Ui,throttle:Ks,uniqueTextMutations:hn},Symbol.toStringTag,{value:"Module"}));var ui="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",mn=typeof Uint8Array>"u"?[]:new Uint8Array(256);for(var de=0;de> 2]; - base64 += chars[(bytes[i2] & 3) << 4 | bytes[i2 + 1] >> 4]; - base64 += chars[(bytes[i2 + 1] & 15) << 2 | bytes[i2 + 2] >> 6]; - base64 += chars[bytes[i2 + 2] & 63]; - } - if (len % 3 === 2) { - base64 = base64.substring(0, base64.length - 1) + "="; - } else if (len % 3 === 1) { - base64 = base64.substring(0, base64.length - 2) + "=="; - } - return base64; - }; - const lastBlobMap = /* @__PURE__ */ new Map(); - const transparentBlobMap = /* @__PURE__ */ new Map(); - async function getTransparentBlobFor(width, height, dataURLOptions) { - const id = \`\${width}-\${height}\`; - if ("OffscreenCanvas" in globalThis) { - if (transparentBlobMap.has(id)) return transparentBlobMap.get(id); - const offscreen = new OffscreenCanvas(width, height); - offscreen.getContext("2d"); - const blob = await offscreen.convertToBlob(dataURLOptions); - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - transparentBlobMap.set(id, base64); - return base64; - } else { - return ""; - } - } - const worker = self; - worker.onmessage = async function(e) { - if ("OffscreenCanvas" in globalThis) { - const { id, bitmap, width, height, dataURLOptions } = e.data; - const transparentBase64 = getTransparentBlobFor( - width, - height, - dataURLOptions - ); - const offscreen = new OffscreenCanvas(width, height); - const ctx = offscreen.getContext("2d"); - ctx.drawImage(bitmap, 0, 0); - bitmap.close(); - const blob = await offscreen.convertToBlob(dataURLOptions); - const type = blob.type; - const arrayBuffer = await blob.arrayBuffer(); - const base64 = encode(arrayBuffer); - if (!lastBlobMap.has(id) && await transparentBase64 === base64) { - lastBlobMap.set(id, base64); - return worker.postMessage({ id }); - } - if (lastBlobMap.get(id) === base64) return worker.postMessage({ id }); - worker.postMessage({ - id, - type, - base64, - width, - height - }); - lastBlobMap.set(id, base64); - } else { - return worker.postMessage({ id: e.data.id }); - } - }; -})(); -//# sourceMappingURL=image-bitmap-data-url-worker-IJpC7g_b.js.map -`;typeof self<"u"&&self.Blob&&new Blob([gn],{type:"text/javascript;charset=utf-8"});try{if(Array.from([1],e=>e*2)[0]!==2){const e=document.createElement("iframe");document.body.appendChild(e),Array.from=((tr=e.contentWindow)==null?void 0:tr.Array.from)||Array.from,document.body.removeChild(e)}}catch(e){console.debug("Unable to override Array.from",e)}ns();var fi;(function(e){e[e.NotStarted=0]="NotStarted",e[e.Running=1]="Running",e[e.Stopped=2]="Stopped"})(fi||(fi={}));class X{constructor(o){le(this,"fileName"),le(this,"functionName"),le(this,"lineNumber"),le(this,"columnNumber"),this.fileName=o.fileName||"",this.functionName=o.functionName||"",this.lineNumber=o.lineNumber,this.columnNumber=o.columnNumber}toString(){const o=this.lineNumber||"",p=this.columnNumber||"";return this.functionName?`${this.functionName} (${this.fileName}:${o}:${p})`:`${this.fileName}:${o}:${p}`}}const yn=/(^|@)\S+:\d+/,hi=/^\s*at .*(\S+:\d+|\(native\))/m,wn=/^(eval@)?(\[native code])?$/,$t={parse:function(e){return e?typeof e.stacktrace<"u"||typeof e["opera#sourceloc"]<"u"?this.parseOpera(e):e.stack&&e.stack.match(hi)?this.parseV8OrIE(e):e.stack?this.parseFFOrSafari(e):(console.warn("[console-record-plugin]: Failed to parse error object:",e),[]):[]},extractLocation:function(e){if(e.indexOf(":")===-1)return[e];const p=/(.+?)(?::(\d+))?(?::(\d+))?$/.exec(e.replace(/[()]/g,""));if(!p)throw new Error(`Cannot parse given url: ${e}`);return[p[1],p[2]||void 0,p[3]||void 0]},parseV8OrIE:function(e){return e.stack.split(` -`).filter(function(p){return!!p.match(hi)},this).map(function(p){p.indexOf("(eval ")>-1&&(p=p.replace(/eval code/g,"eval").replace(/(\(eval at [^()]*)|(\),.*$)/g,""));let d=p.replace(/^\s+/,"").replace(/\(eval code/g,"(");const s=d.match(/ (\((.+):(\d+):(\d+)\)$)/);d=s?d.replace(s[0],""):d;const f=d.split(/\s+/).slice(1),a=this.extractLocation(s?s[1]:f.pop()),m=f.join(" ")||void 0,n=["eval",""].indexOf(a[0])>-1?void 0:a[0];return new X({functionName:m,fileName:n,lineNumber:a[1],columnNumber:a[2]})},this)},parseFFOrSafari:function(e){return e.stack.split(` -`).filter(function(p){return!p.match(wn)},this).map(function(p){if(p.indexOf(" > eval")>-1&&(p=p.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,":$1")),p.indexOf("@")===-1&&p.indexOf(":")===-1)return new X({functionName:p});{const d=/((.*".+"[^@]*)?[^@]*)(?:@)/,s=p.match(d),f=s&&s[1]?s[1]:void 0,a=this.extractLocation(p.replace(d,""));return new X({functionName:f,fileName:a[0],lineNumber:a[1],columnNumber:a[2]})}},this)},parseOpera:function(e){return!e.stacktrace||e.message.indexOf(` -`)>-1&&e.message.split(` -`).length>e.stacktrace.split(` -`).length?this.parseOpera9(e):e.stack?this.parseOpera11(e):this.parseOpera10(e)},parseOpera9:function(e){const o=/Line (\d+).*script (?:in )?(\S+)/i,p=e.message.split(` -`),d=[];for(let s=2,f=p.length;s/,"$2").replace(/\([^)]*\)/g,"")||void 0;return new X({functionName:a,fileName:s[0],lineNumber:s[1],columnNumber:s[2]})},this)}};function bn(e){if(!e||!e.outerHTML)return"";let o="";for(;e.parentElement;){let p=e.localName;if(!p)break;p=p.toLowerCase();const d=e.parentElement,s=[];if(d.children&&d.children.length>0)for(let f=0;f1&&(p+=`:eq(${s.indexOf(e)})`),o=p+(o?">"+o:""),e=d}return o}function Dt(e){return Object.prototype.toString.call(e)==="[object Object]"}function Di(e,o){if(o===0)return!0;const p=Object.keys(e);for(const d of p)if(Dt(e[d])&&Di(e[d],o-1))return!0;return!1}function Z(e,o){const p={numOfKeysLimit:50,depthOfLimit:4};Object.assign(p,o);const d=[],s=[];return JSON.stringify(e,function(m,n){if(d.length>0){const h=d.indexOf(this);~h?d.splice(h+1):d.push(this),~h?s.splice(h,1/0,m):s.push(m),~d.indexOf(n)&&(d[0]===n?n="[Circular ~]":n="[Circular ~."+s.slice(0,d.indexOf(n)).join(".")+"]")}else d.push(n);if(n===null)return n;if(n===void 0)return"undefined";if(f(n))return a(n);if(typeof n=="bigint")return n.toString()+"n";if(n instanceof Event){const h={};for(const t in n){const l=n[t];Array.isArray(l)?h[t]=bn(l.length?l[0]:null):h[t]=l}return h}else{if(n instanceof Node)return n instanceof HTMLElement?n?n.outerHTML:"":n.nodeName;if(n instanceof Error)return n.stack?n.stack+` -End of stack for Error object`:n.name+": "+n.message}return n});function f(m){return!!(Dt(m)&&Object.keys(m).length>p.numOfKeysLimit||typeof m=="function"||Dt(m)&&Di(m,p.depthOfLimit))}function a(m){let n=m.toString();return p.stringLengthLimit&&n.length>p.stringLengthLimit&&(n=`${n.slice(0,p.stringLengthLimit)}...`),n}}const ci={level:["assert","clear","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],lengthThreshold:1e3,logger:"console"};function vn(e,o,p){const d=p?Object.assign({},ci,p):ci,s=d.logger;if(!s)return()=>{};let f;typeof s=="string"?f=o[s]:f=s;let a=0,m=!1;const n=[];if(d.level.includes("error")){const t=i=>{const r=i.message,c=i.error,u=$t.parse(c).map(S=>S.toString()),g=[Z(r,d.stringifyOptions)];e({level:"error",trace:u,payload:g})};o.addEventListener("error",t),n.push(()=>{o.removeEventListener("error",t)});const l=i=>{let r,c;i.reason instanceof Error?(r=i.reason,c=[Z(`Uncaught (in promise) ${r.name}: ${r.message}`,d.stringifyOptions)]):(r=new Error,c=[Z("Uncaught (in promise)",d.stringifyOptions),Z(i.reason,d.stringifyOptions)]);const u=$t.parse(r).map(g=>g.toString());e({level:"error",trace:u,payload:c})};o.addEventListener("unhandledrejection",l),n.push(()=>{o.removeEventListener("unhandledrejection",l)})}for(const t of d.level)n.push(h(f,t));return()=>{n.forEach(t=>t())};function h(t,l){return t[l]?dn.patch(t,l,i=>(...r)=>{if(i.apply(this,r),!(l==="assert"&&r[0])&&!m){m=!0;try{const c=$t.parse(new Error).map(S=>S.toString()).splice(1),g=(l==="assert"?r.slice(1):r).map(S=>Z(S,d.stringifyOptions));a++,a{}}}const xn="rrweb/console@1",Sn=e=>({name:xn,observer:vn,options:e});export{xn as PLUGIN_NAME,Sn as getRecordConsolePlugin}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/entry/app.BLmegFPX.js - -```js -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["_app/immutable/nodes/0.Bx7q24Sj.js","_app/immutable/chunks/CcgZgiL9.js","_app/immutable/chunks/3TW-MQft.js","_app/immutable/chunks/HWAjEwF5.js","_app/immutable/chunks/K4hkKKa0.js","_app/immutable/assets/0.CVzJfwEK.css","_app/immutable/nodes/1.BGCl9XKM.js","_app/immutable/chunks/CpUSghGZ.js","_app/immutable/chunks/CahBYLSE.js","_app/immutable/nodes/2.Co1ksXco.js","_app/immutable/nodes/3.CfAbGAA2.js","_app/immutable/nodes/4.BNJRNjNL.js"])))=>i.map(i=>d[i]); -import{B as Q,d as X,i as x,e as R}from"../chunks/HWAjEwF5.js";import{m as Y,Z as W,z as p,E as $,aA as ee,ad as te,G as re,F as j,as as ae,J as se,e as ne,T as ie,y as oe,i as V,X as F,aB as ue,aC as G,aD as ce,aE as fe,aF as de,g as l,aG as _e,d as O,aH as le,aI as ve,aJ as he,h as me,aK as ge,aL as Ee,R as be,aM as ye,aN as Pe,aO as k,aP as Re,ah as Oe,aQ as Ie,p as Te,M as Ae,u as Se,x as xe,f as T,s as De,a as Le,b as D,c as we,r as Me,t as Be,aR as L}from"../chunks/3TW-MQft.js";import{h as Ne,m as Ce,u as Ye,a as P,c as w,f as z,t as je,s as Ue}from"../chunks/CcgZgiL9.js";import{o as Ve}from"../chunks/CahBYLSE.js";let A=!1;function Fe(t){var e=A;try{return A=!1,[t(),A]}finally{A=e}}function M(t,e,s){var u;Y&&(u=se,W());var o=new Q(t);p(()=>{var r=e()??null;if(Y){var a=ee(u),n=a===ae,c=r!==null;if(n!==c){var m=te();re(m),o.anchor=m,j(!1),o.ensure(r,r&&(f=>s(f,r))),j(!0);return}}o.ensure(r,r&&(f=>s(f,r)))},$)}function U(t,e){return t===e||t?.[G]===e}function B(t={},e,s,u){var o=ne.r,r=F;return ie(()=>{var a,n;return oe(()=>{a=n,n=[],V(()=>{t!==s(...n)&&(e(t,...n),a&&U(s(...a),t)&&e(null,...a))})}),()=>{let c=r;for(;c!==o&&c.parent!==null&&c.parent.f&ue;)c=c.parent;const m=()=>{n&&U(s(...n),t)&&e(null,...n)},f=c.teardown;c.teardown=()=>{m(),f?.()}}}),t}function N(t,e,s,u){var o=!me||(s&ge)!==0,r=(s&he)!==0,a=(s&Pe)!==0,n=u,c=!0,m=()=>(c&&(c=!1,n=a?V(u):u),n);let f;if(r){var S=G in t||k in t;f=ce(t,e)?.set??(S&&e in t?i=>t[e]=i:void 0)}var b,I=!1;r?[b,I]=Fe(()=>t[e]):b=t[e],b===void 0&&u!==void 0&&(b=m(),f&&(o&&fe(),f(b)));var g;if(o?g=()=>{var i=t[e];return i===void 0?m():(c=!0,i)}:g=()=>{var i=t[e];return i!==void 0&&(n=void 0),i===void 0?n:i},o&&(s&de)===0)return g;if(f){var d=t.$$legacy;return(function(i,h){return arguments.length>0?((!o||!h||d||I)&&f(h?g():i),i):g()})}var v=!1,_=((s&Ee)!==0?be:ye)(()=>(v=!1,g()));r&&l(_);var y=F;return(function(i,h){if(arguments.length>0){const E=h?l(_):o&&r?_e(i):i;return O(_,E),v=!0,n!==void 0&&(n=E),i}return le&&v||(y.f&ve)!==0?_.v:l(_)})}function Ge(t){return class extends ke{constructor(e){super({component:t,...e})}}}class ke{#t;#e;constructor(e){var s=new Map,u=(r,a)=>{var n=Ie(a,!1,!1);return s.set(r,n),n};const o=new Proxy({...e.props||{},$$events:{}},{get(r,a){return l(s.get(a)??u(a,Reflect.get(r,a)))},has(r,a){return a===k?!0:(l(s.get(a)??u(a,Reflect.get(r,a))),Reflect.has(r,a))},set(r,a,n){return O(s.get(a)??u(a,n),n),Reflect.set(r,a,n)}});this.#e=(e.hydrate?Ne:Ce)(e.component,{target:e.target,anchor:e.anchor,props:o,context:e.context,intro:e.intro??!1,recover:e.recover,transformError:e.transformError}),(!e?.props?.$$host||e.sync===!1)&&Re(),this.#t=o.$$events;for(const r of Object.keys(this.#e))r==="$set"||r==="$destroy"||r==="$on"||Oe(this,r,{get(){return this.#e[r]},set(a){this.#e[r]=a},enumerable:!0});this.#e.$set=r=>{Object.assign(o,r)},this.#e.$destroy=()=>{Ye(this.#e)}}$set(e){this.#e.$set(e)}$on(e,s){this.#t[e]=this.#t[e]||[];const u=(...o)=>s.call(this,...o);return this.#t[e].push(u),()=>{this.#t[e]=this.#t[e].filter(o=>o!==u)}}$destroy(){this.#e.$destroy()}}const ze=globalThis.__sveltekit_1bdh8ft.env;async function He(){X(ze.PUBLIC_AMPLITUDE_API_KEY??"")}const et={};var Je=z('
'),Ke=z(" ",1);function Ze(t,e){Te(e,!0);let s=N(e,"components",23,()=>[]),u=N(e,"data_0",3,null),o=N(e,"data_1",3,null);Ae(()=>e.stores.page.set(e.page)),Se(()=>{e.stores,e.page,e.constructors,s(),e.form,u(),o(),e.stores.page.notify()});let r=D(!1),a=D(!1),n=D(null);Ve(()=>{const d=e.stores.page.subscribe(()=>{l(r)&&(O(a,!0),xe().then(()=>{O(n,document.title||"untitled page",!0)}))});return O(r,!0),d});const c=L(()=>e.constructors[1]);var m=Ke(),f=T(m);{var S=d=>{const v=L(()=>e.constructors[0]);var _=w(),y=T(_);M(y,()=>l(v),(i,h)=>{B(h(i,{get data(){return u()},get form(){return e.form},get params(){return e.page.params},children:(E,Qe)=>{var C=w(),J=T(C);M(J,()=>l(c),(K,Z)=>{B(Z(K,{get data(){return o()},get form(){return e.form},get params(){return e.page.params}}),q=>s()[1]=q,()=>s()?.[1])}),P(E,C)},$$slots:{default:!0}}),E=>s()[0]=E,()=>s()?.[0])}),P(d,_)},b=d=>{const v=L(()=>e.constructors[0]);var _=w(),y=T(_);M(y,()=>l(v),(i,h)=>{B(h(i,{get data(){return u()},get form(){return e.form},get params(){return e.page.params}}),E=>s()[0]=E,()=>s()?.[0])}),P(d,_)};x(f,d=>{e.constructors[1]?d(S):d(b,-1)})}var I=De(f,2);{var g=d=>{var v=Je(),_=we(v);{var y=i=>{var h=je();Be(()=>Ue(h,l(n))),P(i,h)};x(_,i=>{l(a)&&i(y)})}Me(v),P(d,v)};x(I,d=>{l(r)&&d(g)})}P(t,m),Le()}const tt=Ge(Ze),rt=[()=>R(()=>import("../nodes/0.Bx7q24Sj.js"),__vite__mapDeps([0,1,2,3,4,5])),()=>R(()=>import("../nodes/1.BGCl9XKM.js"),__vite__mapDeps([6,1,2,7,8])),()=>R(()=>import("../nodes/2.Co1ksXco.js"),__vite__mapDeps([9,1,2,3,4])),()=>R(()=>import("../nodes/3.CfAbGAA2.js"),__vite__mapDeps([10,1,2,3,7,8,4])),()=>R(()=>import("../nodes/4.BNJRNjNL.js"),__vite__mapDeps([11,1,2,3,7,8,4]))],at=[],st={"/":[2],"/burrito":[3],"/profile":[4]},H={handleError:(({error:t})=>{console.error(t)}),init:He,reroute:(()=>{}),transport:{}},qe=Object.fromEntries(Object.entries(H.transport).map(([t,e])=>[t,e.decode])),nt=Object.fromEntries(Object.entries(H.transport).map(([t,e])=>[t,e.encode])),it=!1,ot=(t,e)=>qe[t](e);export{ot as decode,qe as decoders,st as dictionary,nt as encoders,it as hash,H as hooks,et as matchers,rt as nodes,tt as root,at as server_loads}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/entry/start.C-_kLNuo.js - -```js -import{l as o,a as r}from"../chunks/CpUSghGZ.js";export{o as load_css,r as start}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/nodes/0.Bx7q24Sj.js - -```js -import{d as N,a as v,f as p,s as P,b as S}from"../chunks/CcgZgiL9.js";import{z as k,E as O,A as $,H as q,B as z,m as h,C as G,D as b,F as y,G as E,J,K,e as L,M as Q,u as x,i as V,O as A,P as W,g as F,Q as j,R as I,S as U,p as w,a as B,c as u,s as _,r as d,n as X,f as R,t as Y,T as Z,$ as ee}from"../chunks/3TW-MQft.js";import{B as ae,i as C}from"../chunks/HWAjEwF5.js";import{g as te,A as se,s as re}from"../chunks/K4hkKKa0.js";function ne(n,s,...a){var r=new ae(n);k(()=>{const t=s()??null;r.ensure(t,t&&(e=>t(e,...a)))},O)}function oe(n,s){let a=null,r=h;var t;if(h){a=J;for(var e=K(document.head);e!==null&&(e.nodeType!==G||e.data!==n);)e=b(e);if(e===null)y(!1);else{var i=b(e);e.remove(),E(i)}}h||(t=document.head.appendChild($()));try{k(()=>s(t),q|z)}finally{r&&(y(!0),E(a))}}function ie(n=!1){const s=L,a=s.l.u;if(!a)return;let r=()=>j(s.s);if(n){let t=0,e={};const i=I(()=>{let f=!1;const o=s.s;for(const c in o)o[c]!==e[c]&&(e[c]=o[c],f=!0);return f&&t++,t});r=()=>F(i)}a.b.length&&Q(()=>{T(s,r),A(a.b)}),x(()=>{const t=V(()=>a.m.map(W));return()=>{for(const e of t)typeof e=="function"&&e()}}),a.a.length&&x(()=>{T(s,r),A(a.a)})}function T(n,s){if(n.l.s)for(const a of n.l.s)F(a);s()}U();var le=p('Burrito Profile',1),fe=p(' ',1),ce=p('
');function ue(n,s){w(s,!1);const a=te();ie();var r=ce(),t=u(r),e=u(t),i=_(u(e),2);{var f=l=>{var m=le();X(2),v(l,m)};C(i,l=>{a.user&&l(f)})}d(e);var o=_(e,2),c=u(o);{var D=l=>{var m=fe(),g=R(m),H=u(g);d(g);var M=_(g,2);Y(()=>P(H,`Welcome, ${a.user.username??""}`)),S("click",M,()=>a.logout()),v(l,m)};C(c,l=>{a.user&&l(D)})}d(o),d(t),d(r),v(n,r),B()}N(["click"]);var de=p(''),ve=p("
",1);function he(n,s){w(s,!0);const a=new se;re(a);var r=ve();oe("12qhfyh",f=>{var o=de();Z(()=>{ee.title="Burrito consideration app"}),v(f,o)});var t=R(r);ue(t,{});var e=_(t,2),i=u(e);ne(i,()=>s.children),d(e),v(n,r),B()}export{he as component}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/nodes/1.BGCl9XKM.js - -```js -import{a as h,f as i,s}from"../chunks/CcgZgiL9.js";import{p as g,f as v,t as d,a as l,c as a,r as o,s as _}from"../chunks/3TW-MQft.js";import{s as x,p}from"../chunks/CpUSghGZ.js";const $={get error(){return p.error},get status(){return p.status}};x.updated.check;const c=$;var k=i("

",1);function q(m,f){g(f,!0);var t=k(),r=v(t),n=a(r,!0);o(r);var e=_(r,2),u=a(e,!0);o(e),d(()=>{s(n,c.status),s(u,c.error?.message)}),h(m,t),l()}export{q as component}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/nodes/2.Co1ksXco.js - -```js -import{a as S,f as k,s as C,e as K}from"../chunks/CcgZgiL9.js";import{m as T,q as R,o as Y,L as X,N as j,I as z,v as F,w as b,x as J,i as Q,y as Z,p as $,a as ee,c as d,r as m,f as q,n as E,t as H,s as p,g as _,b as L,d as c}from"../chunks/3TW-MQft.js";import{i as O}from"../chunks/HWAjEwF5.js";import{g as ae}from"../chunks/K4hkKKa0.js";const re=Symbol("is custom element"),se=Symbol("is html"),oe=z?"link":"LINK";function P(e){if(T){var a=!1,o=()=>{if(!a){if(a=!0,e.hasAttribute("value")){var t=e.value;U(e,"value",null),e.value=t}if(e.hasAttribute("checked")){var r=e.checked;U(e,"checked",null),e.checked=r}}};e.__on_r=o,R(o),Y()}}function U(e,a,o,t){var r=te(e);T&&(r[a]=e.getAttribute(a),a==="src"||a==="srcset"||a==="href"&&e.nodeName===oe)||r[a]!==(r[a]=o)&&(a==="loading"&&(e[X]=o),e.removeAttribute(a))}function te(e){return e.__attributes??={[re]:e.nodeName.includes("-"),[se]:e.namespaceURI===j}}function V(e,a,o=a){var t=new WeakSet;F(e,"input",async r=>{var s=r?e.defaultValue:e.value;if(s=x(e)?I(s):s,o(s),b!==null&&t.add(b),await J(),s!==(s=a())){var g=e.selectionStart,l=e.selectionEnd,y=e.value.length;if(e.value=s??"",l!==null){var u=e.value.length;g===l&&l===y&&u>y?(e.selectionStart=u,e.selectionEnd=u):(e.selectionStart=g,e.selectionEnd=Math.min(l,u))}}}),(T&&e.defaultValue!==e.value||Q(a)==null&&e.value)&&(o(x(e)?I(e.value):e.value),b!==null&&t.add(b)),Z(()=>{var r=a();if(e===document.activeElement){var s=b;if(t.has(s))return}x(e)&&r===I(e.value)||e.type==="date"&&!r&&!e.value||r!==e.value&&(e.value=r??"")})}function x(e){var a=e.type;return a==="number"||a==="range"}function I(e){return e===""?null:+e}var le=k('

You are now logged in. Check out the navigation to explore features.

',1),ie=k('

'),ne=k('

Welcome to Burrito consideration app

Sign in to start considering burritos.

Enter any username and password to sign in. This is a demo app.

',1),ve=k('
');function me(e,a){$(a,!0);const o=ae();let t=L(""),r=L(""),s=L("");async function g(i){i.preventDefault(),c(s,"");try{await o.login(_(t),_(r))?(c(t,""),c(r,"")):c(s,"Please provide both username and password")}catch(n){console.error("Login failed:",n),c(s,"An error occurred during login")}}var l=ve(),y=d(l);{var u=i=>{var n=le(),f=q(n),h=d(f);m(f),E(4),H(()=>C(h,`Welcome back, ${o.user.username??""}!`)),S(i,n)},W=i=>{var n=ne(),f=p(q(n),4),h=d(f),M=p(d(h),2);P(M),m(h);var w=p(h,2),N=p(d(w),2);P(N),m(w);var B=p(w,2);{var D=v=>{var A=ie(),G=d(A,!0);m(A),H(()=>C(G,_(s))),S(v,A)};O(B,v=>{_(s)&&v(D)})}E(2),m(f),E(2),K("submit",f,g),V(M,()=>_(t),v=>c(t,v)),V(N,()=>_(r),v=>c(r,v)),S(i,n)};O(y,i=>{o.user?i(u):i(W,-1)})}m(l),S(e,l),ee()}export{me as component}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/nodes/3.CfAbGAA2.js - -```js -import{d as E,a as s,f as n,s as m,b as I}from"../chunks/CcgZgiL9.js";import{p as P,u as j,a as q,c as o,r as a,s as i,f as D,g as F,n as G,t as h,b as H,d as b}from"../chunks/3TW-MQft.js";import{i as _,t as J}from"../chunks/HWAjEwF5.js";import{g as K}from"../chunks/CpUSghGZ.js";import{g as L}from"../chunks/K4hkKKa0.js";var M=n('

'),N=n('

Burrito consideration zone

This is where you consider the infinite potential of burritos.

Current considerations:

Each consideration is tracked as an Amplitude event with custom properties.

',1),O=n("

Please log in to consider burritos.

"),Q=n('
');function X(g,C){P(C,!0);const t=L();let u=H(!1);j(()=>{t.user||K("/")});function k(){t.user&&(t.incrementBurritoConsiderations(),b(u,!0),setTimeout(()=>b(u,!1),2e3),J("burrito_considered",{total_considerations:t.user.burritoConsiderations,username:t.user.username}))}var c=Q(),x=o(c);{var y=r=>{var e=N(),d=i(D(e),4),l=i(o(d)),w=o(l,!0);a(l),a(d);var v=i(d,2),A=i(v,2);{var B=p=>{var f=M(),z=o(f);a(f),h(()=>m(z,`Thank you for your consideration! Count: ${t.user.burritoConsiderations??""}`)),s(p,f)};_(A,p=>{F(u)&&p(B)})}G(2),h(()=>m(w,t.user.burritoConsiderations)),I("click",v,k),s(r,e)},T=r=>{var e=O();s(r,e)};_(x,r=>{t.user?r(y):r(T,-1)})}a(c),s(g,c),q()}E(["click"]);export{X as component}; - -``` - ---- - -## .svelte-kit/output/client/_app/immutable/nodes/4.BNJRNjNL.js - -```js -import{a as n,f,s as u}from"../chunks/CcgZgiL9.js";import{p as b,u as C,a as U,c as a,r as e,s as t,f as k,t as w}from"../chunks/3TW-MQft.js";import{i as y}from"../chunks/HWAjEwF5.js";import{g as A}from"../chunks/CpUSghGZ.js";import{g as B}from"../chunks/K4hkKKa0.js";var P=f('

User profile

Your information

Username:

Burrito considerations:

',1),Y=f("

Please log in to view your profile.

"),j=f('
');function G(c,l){b(l,!0);const o=B();C(()=>{o.user||A("/")});var i=j(),_=a(i);{var g=r=>{var s=P(),v=t(k(s),2),p=t(a(v),2),h=t(a(p));e(p);var m=t(p,2),x=t(a(m));e(m),e(v),w(()=>{u(h,` ${o.user.username??""}`),u(x,` ${o.user.burritoConsiderations??""}`)}),n(r,s)},d=r=>{var s=Y();n(r,s)};y(_,r=>{o.user?r(g):r(d,-1)})}e(i),n(c,i),U()}export{G as component}; - -``` - ---- - -## .svelte-kit/output/client/robots.txt - -```txt -# allow crawling everything by default -User-agent: * -Disallow: - -``` - ---- - -## .svelte-kit/output/server/chunks/attributes.js - -```js -import { l as lifecycle_outside_component } from "./errors.js"; -var ssr_context = null; -function set_ssr_context(v) { - ssr_context = v; -} -function getContext(key) { - const context_map = get_or_init_context_map(); - const result = ( - /** @type {T} */ - context_map.get(key) - ); - return result; -} -function setContext(key, context) { - get_or_init_context_map().set(key, context); - return context; -} -function get_or_init_context_map(name) { - if (ssr_context === null) { - lifecycle_outside_component(); - } - return ssr_context.c ??= new Map(get_parent_context(ssr_context) || void 0); -} -function push(fn) { - ssr_context = { p: ssr_context, c: null, r: null }; -} -function pop() { - ssr_context = /** @type {SSRContext} */ - ssr_context.p; -} -function get_parent_context(ssr_context2) { - let parent = ssr_context2.p; - while (parent !== null) { - const context_map = parent.c; - if (context_map !== null) { - return context_map; - } - parent = parent.p; - } - return null; -} -var is_array = Array.isArray; -var index_of = Array.prototype.indexOf; -var includes = Array.prototype.includes; -var array_from = Array.from; -var define_property = Object.defineProperty; -var get_descriptor = Object.getOwnPropertyDescriptor; -var object_prototype = Object.prototype; -var array_prototype = Array.prototype; -var get_prototype_of = Object.getPrototypeOf; -var is_extensible = Object.isExtensible; -var has_own_property = Object.prototype.hasOwnProperty; -const noop = () => { -}; -function run_all(arr) { - for (var i = 0; i < arr.length; i++) { - arr[i](); - } -} -function deferred() { - var resolve; - var reject; - var promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { promise, resolve, reject }; -} -const ATTR_REGEX = /[&"<]/g; -const CONTENT_REGEX = /[&<]/g; -function escape_html(value, is_attr) { - const str = String(value ?? ""); - const pattern = is_attr ? ATTR_REGEX : CONTENT_REGEX; - pattern.lastIndex = 0; - let escaped = ""; - let last = 0; - while (pattern.test(str)) { - const i = pattern.lastIndex - 1; - const ch = str[i]; - escaped += str.substring(last, i) + (ch === "&" ? "&" : ch === '"' ? """ : "<"); - last = i + 1; - } - return escaped + str.substring(last); -} -function r(e) { - var t, f, n = ""; - if ("string" == typeof e || "number" == typeof e) n += e; - else if ("object" == typeof e) if (Array.isArray(e)) { - var o = e.length; - for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f); - } else for (f in e) e[f] && (n && (n += " "), n += f); - return n; -} -function clsx$1() { - for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t); - return n; -} -const replacements = { - translate: /* @__PURE__ */ new Map([ - [true, "yes"], - [false, "no"] - ]) -}; -function attr(name, value, is_boolean = false) { - if (name === "hidden" && value !== "until-found") { - is_boolean = true; - } - if (value == null || !value && is_boolean) return ""; - const normalized = has_own_property.call(replacements, name) && replacements[name].get(value) || value; - const assignment = is_boolean ? `=""` : `="${escape_html(normalized, true)}"`; - return ` ${name}${assignment}`; -} -function clsx(value) { - if (typeof value === "object") { - return clsx$1(value); - } else { - return value ?? ""; - } -} -const whitespace = [..." \n\r\f \v\uFEFF"]; -function to_class(value, hash, directives) { - var classname = value == null ? "" : "" + value; - if (hash) { - classname = classname ? classname + " " + hash : hash; - } - if (directives) { - for (var key of Object.keys(directives)) { - if (directives[key]) { - classname = classname ? classname + " " + key : key; - } else if (classname.length) { - var len = key.length; - var a = 0; - while ((a = classname.indexOf(key, a)) >= 0) { - var b = a + len; - if ((a === 0 || whitespace.includes(classname[a - 1])) && (b === classname.length || whitespace.includes(classname[b]))) { - classname = (a === 0 ? "" : classname.substring(0, a)) + classname.substring(b + 1); - } else { - a = b; - } - } - } - } - } - return classname === "" ? null : classname; -} -function append_styles(styles, important = false) { - var separator = important ? " !important;" : ";"; - var css = ""; - for (var key of Object.keys(styles)) { - var value = styles[key]; - if (value != null && value !== "") { - css += " " + key + ": " + value + separator; - } - } - return css; -} -function to_css_name(name) { - if (name[0] !== "-" || name[1] !== "-") { - return name.toLowerCase(); - } - return name; -} -function to_style(value, styles) { - if (styles) { - var new_style = ""; - var normal_styles; - var important_styles; - if (Array.isArray(styles)) { - normal_styles = styles[0]; - important_styles = styles[1]; - } else { - normal_styles = styles; - } - if (value) { - value = String(value).replaceAll(/\s*\/\*.*?\*\/\s*/g, "").trim(); - var in_str = false; - var in_apo = 0; - var in_comment = false; - var reserved_names = []; - if (normal_styles) { - reserved_names.push(...Object.keys(normal_styles).map(to_css_name)); - } - if (important_styles) { - reserved_names.push(...Object.keys(important_styles).map(to_css_name)); - } - var start_index = 0; - var name_index = -1; - const len = value.length; - for (var i = 0; i < len; i++) { - var c = value[i]; - if (in_comment) { - if (c === "/" && value[i - 1] === "*") { - in_comment = false; - } - } else if (in_str) { - if (in_str === c) { - in_str = false; - } - } else if (c === "/" && value[i + 1] === "*") { - in_comment = true; - } else if (c === '"' || c === "'") { - in_str = c; - } else if (c === "(") { - in_apo++; - } else if (c === ")") { - in_apo--; - } - if (!in_comment && in_str === false && in_apo === 0) { - if (c === ":" && name_index === -1) { - name_index = i; - } else if (c === ";" || i === len - 1) { - if (name_index !== -1) { - var name = to_css_name(value.substring(start_index, name_index).trim()); - if (!reserved_names.includes(name)) { - if (c !== ";") { - i++; - } - var property = value.substring(start_index, i).trim(); - new_style += " " + property + ";"; - } - } - start_index = i + 1; - name_index = -1; - } - } - } - } - if (normal_styles) { - new_style += append_styles(normal_styles); - } - if (important_styles) { - new_style += append_styles(important_styles, true); - } - new_style = new_style.trim(); - return new_style === "" ? null : new_style; - } - return value == null ? null : String(value); -} -export { - attr as a, - array_prototype as b, - get_descriptor as c, - deferred as d, - escape_html as e, - get_prototype_of as f, - getContext as g, - is_array as h, - includes as i, - is_extensible as j, - index_of as k, - define_property as l, - array_from as m, - noop as n, - object_prototype as o, - clsx as p, - to_class as q, - run_all as r, - setContext as s, - to_style as t, - ssr_context as u, - set_ssr_context as v, - push as w, - pop as x, - has_own_property as y -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/auth.svelte.js - -```js -import { B as BROWSER } from "./errors.js"; -import { s as setContext, g as getContext } from "./attributes.js"; -import * as amplitude from "@amplitude/unified"; -import { Identify } from "@amplitude/unified"; -const AUTH_KEY = /* @__PURE__ */ Symbol("auth"); -class AuthState { - user = null; - constructor() { - } - login = async (username, password) => { - try { - const response = await fetch("/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ username, password }) - }); - if (response.ok) { - const { user: userData } = await response.json(); - this.user = userData; - if (BROWSER) ; - return true; - } - return false; - } catch (error) { - console.error("Login error:", error); - return false; - } - }; - logout = () => { - this.user = null; - }; - incrementBurritoConsiderations = () => { - if (this.user) { - this.user = { - ...this.user, - burritoConsiderations: this.user.burritoConsiderations + 1 - }; - } - }; -} -function setAuthContext(auth) { - setContext(AUTH_KEY, auth); -} -function getAuthContext() { - return getContext(AUTH_KEY); -} -export { - AuthState as A, - getAuthContext as g, - setAuthContext as s -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/environment.js - -```js -let base = ""; -let assets = base; -const app_dir = "_app"; -const relative = false; -const initial = { base, assets }; -function override(paths) { - base = paths.base; - assets = paths.assets; -} -function reset() { - base = initial.base; - assets = initial.assets; -} -function set_assets(path) { - assets = initial.assets = path; -} -let prerendering = false; -function set_building() { -} -function set_prerendering() { - prerendering = true; -} -export { - assets as a, - base as b, - app_dir as c, - reset as d, - set_building as e, - set_prerendering as f, - override as o, - prerendering as p, - relative as r, - set_assets as s -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/errors.js - -```js -const BROWSER = false; -function experimental_async_required(name) { - { - throw new Error(`https://svelte.dev/e/experimental_async_required`); - } -} -function lifecycle_outside_component(name) { - { - throw new Error(`https://svelte.dev/e/lifecycle_outside_component`); - } -} -export { - BROWSER as B, - experimental_async_required as e, - lifecycle_outside_component as l -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/exports.js - -```js -const SCHEME = /^[a-z][a-z\d+\-.]+:/i; -const internal = new URL("sveltekit-internal://"); -function resolve(base, path) { - if (path[0] === "/" && path[1] === "/") return path; - let url = new URL(base, internal); - url = new URL(path, url); - return url.protocol === internal.protocol ? url.pathname + url.search + url.hash : url.href; -} -function normalize_path(path, trailing_slash) { - if (path === "/" || trailing_slash === "ignore") return path; - if (trailing_slash === "never") { - return path.endsWith("/") ? path.slice(0, -1) : path; - } else if (trailing_slash === "always" && !path.endsWith("/")) { - return path + "/"; - } - return path; -} -function decode_pathname(pathname) { - return pathname.split("%25").map(decodeURI).join("%25"); -} -function decode_params(params) { - for (const key in params) { - params[key] = decodeURIComponent(params[key]); - } - return params; -} -function make_trackable(url, callback, search_params_callback, allow_hash = false) { - const tracked = new URL(url); - Object.defineProperty(tracked, "searchParams", { - value: new Proxy(tracked.searchParams, { - get(obj, key) { - if (key === "get" || key === "getAll" || key === "has") { - return (param, ...rest) => { - search_params_callback(param); - return obj[key](param, ...rest); - }; - } - callback(); - const value = Reflect.get(obj, key); - return typeof value === "function" ? value.bind(obj) : value; - } - }), - enumerable: true, - configurable: true - }); - const tracked_url_properties = ["href", "pathname", "search", "toString", "toJSON"]; - if (allow_hash) tracked_url_properties.push("hash"); - for (const property of tracked_url_properties) { - Object.defineProperty(tracked, property, { - get() { - callback(); - return url[property]; - }, - enumerable: true, - configurable: true - }); - } - { - tracked[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { - return inspect(url, opts); - }; - tracked.searchParams[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { - return inspect(url.searchParams, opts); - }; - } - if (!allow_hash) { - disable_hash(tracked); - } - return tracked; -} -function disable_hash(url) { - allow_nodejs_console_log(url); - Object.defineProperty(url, "hash", { - get() { - throw new Error( - "Cannot access event.url.hash. Consider using `page.url.hash` inside a component instead" - ); - } - }); -} -function disable_search(url) { - allow_nodejs_console_log(url); - for (const property of ["search", "searchParams"]) { - Object.defineProperty(url, property, { - get() { - throw new Error(`Cannot access url.${property} on a page with prerendering enabled`); - } - }); - } -} -function allow_nodejs_console_log(url) { - { - url[/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")] = (depth, opts, inspect) => { - return inspect(new URL(url), opts); - }; - } -} -function validator(expected) { - function validate(module, file) { - if (!module) return; - for (const key in module) { - if (key[0] === "_" || expected.has(key)) continue; - const values = [...expected.values()]; - const hint = hint_for_supported_files(key, file?.slice(file.lastIndexOf("."))) ?? `valid exports are ${values.join(", ")}, or anything with a '_' prefix`; - throw new Error(`Invalid export '${key}'${file ? ` in ${file}` : ""} (${hint})`); - } - } - return validate; -} -function hint_for_supported_files(key, ext = ".js") { - const supported_files = []; - if (valid_layout_exports.has(key)) { - supported_files.push(`+layout${ext}`); - } - if (valid_page_exports.has(key)) { - supported_files.push(`+page${ext}`); - } - if (valid_layout_server_exports.has(key)) { - supported_files.push(`+layout.server${ext}`); - } - if (valid_page_server_exports.has(key)) { - supported_files.push(`+page.server${ext}`); - } - if (valid_server_exports.has(key)) { - supported_files.push(`+server${ext}`); - } - if (supported_files.length > 0) { - return `'${key}' is a valid export in ${supported_files.slice(0, -1).join(", ")}${supported_files.length > 1 ? " or " : ""}${supported_files.at(-1)}`; - } -} -const valid_layout_exports = /* @__PURE__ */ new Set([ - "load", - "prerender", - "csr", - "ssr", - "trailingSlash", - "config" -]); -const valid_page_exports = /* @__PURE__ */ new Set([...valid_layout_exports, "entries"]); -const valid_layout_server_exports = /* @__PURE__ */ new Set([...valid_layout_exports]); -const valid_page_server_exports = /* @__PURE__ */ new Set([...valid_layout_server_exports, "actions", "entries"]); -const valid_server_exports = /* @__PURE__ */ new Set([ - "GET", - "POST", - "PATCH", - "PUT", - "DELETE", - "OPTIONS", - "HEAD", - "fallback", - "prerender", - "trailingSlash", - "config", - "entries" -]); -const validate_layout_exports = validator(valid_layout_exports); -const validate_page_exports = validator(valid_page_exports); -const validate_layout_server_exports = validator(valid_layout_server_exports); -const validate_page_server_exports = validator(valid_page_server_exports); -const validate_server_exports = validator(valid_server_exports); -export { - SCHEME as S, - decode_params as a, - validate_layout_exports as b, - validate_page_server_exports as c, - disable_search as d, - validate_page_exports as e, - decode_pathname as f, - validate_server_exports as g, - make_trackable as m, - normalize_path as n, - resolve as r, - validate_layout_server_exports as v -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/index.js - -```js -import { n as noop } from "./attributes.js"; -import { s as safe_not_equal } from "./root.js"; -const subscriber_queue = []; -function readable(value, start) { - return { - subscribe: writable(value, start).subscribe - }; -} -function writable(value, start = noop) { - let stop = null; - const subscribers = /* @__PURE__ */ new Set(); - function set(new_value) { - if (safe_not_equal(value, new_value)) { - value = new_value; - if (stop) { - const run_queue = !subscriber_queue.length; - for (const subscriber of subscribers) { - subscriber[1](); - subscriber_queue.push(subscriber, value); - } - if (run_queue) { - for (let i = 0; i < subscriber_queue.length; i += 2) { - subscriber_queue[i][0](subscriber_queue[i + 1]); - } - subscriber_queue.length = 0; - } - } - } - } - function update(fn) { - set(fn( - /** @type {T} */ - value - )); - } - function subscribe(run, invalidate = noop) { - const subscriber = [run, invalidate]; - subscribers.add(subscriber); - if (subscribers.size === 1) { - stop = start(set, update) || noop; - } - run( - /** @type {T} */ - value - ); - return () => { - subscribers.delete(subscriber); - if (subscribers.size === 0 && stop) { - stop(); - stop = null; - } - }; - } - return { set, update, subscribe }; -} -export { - readable as r, - writable as w -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/internal.js - -```js -import { r as root } from "./root.js"; -import "./environment.js"; -import "./shared-server.js"; -let read_implementation = null; -function set_read_implementation(fn) { - read_implementation = fn; -} -function set_manifest(_) { -} -const options = { - app_template_contains_nonce: false, - async: false, - csp: { "mode": "auto", "directives": { "upgrade-insecure-requests": false, "block-all-mixed-content": false }, "reportOnly": { "upgrade-insecure-requests": false, "block-all-mixed-content": false } }, - csrf_check_origin: true, - csrf_trusted_origins: [], - embedded: false, - env_public_prefix: "PUBLIC_", - env_private_prefix: "", - hash_routing: false, - hooks: null, - // added lazily, via `get_hooks` - preload_strategy: "modulepreload", - root, - service_worker: false, - service_worker_options: void 0, - server_error_boundaries: false, - templates: { - app: ({ head, body, assets, nonce, env }) => '\n\n \n \n \n ' + head + '\n \n \n
' + body + "
\n \n\n", - error: ({ status, message }) => '\n\n \n \n ' + message + ` - - - - -
- ` + status + '\n
\n

' + message + "

\n
\n
\n \n\n" - }, - version_hash: "1bdh8ft" -}; -async function get_hooks() { - let handle; - let handleFetch; - let handleError; - let handleValidationError; - let init; - ({ handle, handleFetch, handleError, handleValidationError, init } = await import("../entries/hooks.server.js")); - let reroute; - let transport; - return { - handle, - handleFetch, - handleError, - handleValidationError, - init, - reroute, - transport - }; -} -export { - set_manifest as a, - get_hooks as g, - options as o, - read_implementation as r, - set_read_implementation as s -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/render-context.js - -```js -const escaped = { - "<": "\\u003C", - "\\": "\\\\", - "\b": "\\b", - "\f": "\\f", - "\n": "\\n", - "\r": "\\r", - " ": "\\t", - "\u2028": "\\u2028", - "\u2029": "\\u2029" -}; -class DevalueError extends Error { - /** - * @param {string} message - * @param {string[]} keys - * @param {any} [value] - The value that failed to be serialized - * @param {any} [root] - The root value being serialized - */ - constructor(message, keys, value, root) { - super(message); - this.name = "DevalueError"; - this.path = keys.join(""); - this.value = value; - this.root = root; - } -} -function is_primitive(thing) { - return thing === null || typeof thing !== "object" && typeof thing !== "function"; -} -const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(Object.prototype).sort().join("\0"); -function is_plain_object(thing) { - const proto = Object.getPrototypeOf(thing); - return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null || Object.getOwnPropertyNames(proto).sort().join("\0") === object_proto_names; -} -function get_type(thing) { - return Object.prototype.toString.call(thing).slice(8, -1); -} -function get_escaped_char(char) { - switch (char) { - case '"': - return '\\"'; - case "<": - return "\\u003C"; - case "\\": - return "\\\\"; - case "\n": - return "\\n"; - case "\r": - return "\\r"; - case " ": - return "\\t"; - case "\b": - return "\\b"; - case "\f": - return "\\f"; - case "\u2028": - return "\\u2028"; - case "\u2029": - return "\\u2029"; - default: - return char < " " ? `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}` : ""; - } -} -function stringify_string(str) { - let result = ""; - let last_pos = 0; - const len = str.length; - for (let i = 0; i < len; i += 1) { - const char = str[i]; - const replacement = get_escaped_char(char); - if (replacement) { - result += str.slice(last_pos, i) + replacement; - last_pos = i + 1; - } - } - return `"${last_pos === 0 ? str : result + str.slice(last_pos)}"`; -} -function enumerable_symbols(object) { - return Object.getOwnPropertySymbols(object).filter( - (symbol) => Object.getOwnPropertyDescriptor(object, symbol).enumerable - ); -} -const is_identifier = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/; -function stringify_key(key) { - return is_identifier.test(key) ? "." + key : "[" + JSON.stringify(key) + "]"; -} -function is_valid_array_index(s) { - if (s.length === 0) return false; - if (s.length > 1 && s.charCodeAt(0) === 48) return false; - for (let i = 0; i < s.length; i++) { - const c = s.charCodeAt(i); - if (c < 48 || c > 57) return false; - } - const n = +s; - if (n >= 2 ** 32 - 1) return false; - if (n < 0) return false; - return true; -} -function valid_array_indices(array) { - const keys = Object.keys(array); - for (var i = keys.length - 1; i >= 0; i--) { - if (is_valid_array_index(keys[i])) { - break; - } - } - keys.length = i + 1; - return keys; -} -const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$"; -const unsafe_chars = /[<\b\f\n\r\t\0\u2028\u2029]/g; -const reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/; -function uneval(value, replacer) { - const counts = /* @__PURE__ */ new Map(); - const keys = []; - const custom = /* @__PURE__ */ new Map(); - function walk(thing) { - if (!is_primitive(thing)) { - if (counts.has(thing)) { - counts.set(thing, counts.get(thing) + 1); - return; - } - counts.set(thing, 1); - if (replacer) { - const str2 = replacer(thing, (value2) => uneval(value2, replacer)); - if (typeof str2 === "string") { - custom.set(thing, str2); - return; - } - } - if (typeof thing === "function") { - throw new DevalueError(`Cannot stringify a function`, keys, thing, value); - } - const type = get_type(thing); - switch (type) { - case "Number": - case "BigInt": - case "String": - case "Boolean": - case "Date": - case "RegExp": - case "URL": - case "URLSearchParams": - return; - case "Array": - thing.forEach((value2, i) => { - keys.push(`[${i}]`); - walk(value2); - keys.pop(); - }); - break; - case "Set": - Array.from(thing).forEach(walk); - break; - case "Map": - for (const [key, value2] of thing) { - keys.push(`.get(${is_primitive(key) ? stringify_primitive(key) : "..."})`); - walk(value2); - keys.pop(); - } - break; - case "Int8Array": - case "Uint8Array": - case "Uint8ClampedArray": - case "Int16Array": - case "Uint16Array": - case "Float16Array": - case "Int32Array": - case "Uint32Array": - case "Float32Array": - case "Float64Array": - case "BigInt64Array": - case "BigUint64Array": - case "DataView": - walk(thing.buffer); - return; - case "ArrayBuffer": - return; - case "Temporal.Duration": - case "Temporal.Instant": - case "Temporal.PlainDate": - case "Temporal.PlainTime": - case "Temporal.PlainDateTime": - case "Temporal.PlainMonthDay": - case "Temporal.PlainYearMonth": - case "Temporal.ZonedDateTime": - return; - default: - if (!is_plain_object(thing)) { - throw new DevalueError(`Cannot stringify arbitrary non-POJOs`, keys, thing, value); - } - if (enumerable_symbols(thing).length > 0) { - throw new DevalueError(`Cannot stringify POJOs with symbolic keys`, keys, thing, value); - } - for (const key of Object.keys(thing)) { - if (key === "__proto__") { - throw new DevalueError( - `Cannot stringify objects with __proto__ keys`, - keys, - thing, - value - ); - } - keys.push(stringify_key(key)); - walk(thing[key]); - keys.pop(); - } - } - } else if (typeof thing === "symbol") { - throw new DevalueError(`Cannot stringify a Symbol primitive`, keys, thing, value); - } - } - walk(value); - const names = /* @__PURE__ */ new Map(); - Array.from(counts).filter((entry) => entry[1] > 1).sort((a, b) => b[1] - a[1]).forEach((entry, i) => { - names.set(entry[0], get_name(i)); - }); - function stringify(thing) { - if (names.has(thing)) { - return names.get(thing); - } - if (is_primitive(thing)) { - return stringify_primitive(thing); - } - if (custom.has(thing)) { - return custom.get(thing); - } - const type = get_type(thing); - switch (type) { - case "Number": - case "String": - case "Boolean": - case "BigInt": - return `Object(${stringify(thing.valueOf())})`; - case "RegExp": - return `new RegExp(${stringify_string(thing.source)}, "${thing.flags}")`; - case "Date": - return `new Date(${thing.getTime()})`; - case "URL": - return `new URL(${stringify_string(thing.toString())})`; - case "URLSearchParams": - return `new URLSearchParams(${stringify_string(thing.toString())})`; - case "Array": { - let has_holes = false; - let result = "["; - for (let i = 0; i < thing.length; i += 1) { - if (i > 0) result += ","; - if (Object.hasOwn(thing, i)) { - result += stringify(thing[i]); - } else if (!has_holes) { - const populated_keys = valid_array_indices( - /** @type {any[]} */ - thing - ); - const population = populated_keys.length; - const d = String(thing.length).length; - const hole_cost = thing.length + 2; - const sparse_cost = 25 + d + population * (d + 2); - if (hole_cost > sparse_cost) { - const entries = populated_keys.map((k) => `${k}:${stringify(thing[k])}`).join(","); - return `Object.assign(Array(${thing.length}),{${entries}})`; - } - has_holes = true; - i -= 1; - } - } - const tail = thing.length === 0 || thing.length - 1 in thing ? "" : ","; - return result + tail + "]"; - } - case "Set": - case "Map": - return `new ${type}([${Array.from(thing).map(stringify).join(",")}])`; - case "Int8Array": - case "Uint8Array": - case "Uint8ClampedArray": - case "Int16Array": - case "Uint16Array": - case "Float16Array": - case "Int32Array": - case "Uint32Array": - case "Float32Array": - case "Float64Array": - case "BigInt64Array": - case "BigUint64Array": { - let str2 = `new ${type}`; - if (!names.has(thing.buffer)) { - const array = new thing.constructor(thing.buffer); - str2 += `([${array}])`; - } else { - str2 += `(${stringify(thing.buffer)})`; - } - if (thing.byteLength !== thing.buffer.byteLength) { - const start = thing.byteOffset / thing.BYTES_PER_ELEMENT; - const end = start + thing.length; - str2 += `.subarray(${start},${end})`; - } - return str2; - } - case "DataView": { - let str2 = `new DataView`; - if (!names.has(thing.buffer)) { - str2 += `(new Uint8Array([${new Uint8Array(thing.buffer)}]).buffer`; - } else { - str2 += `(${stringify(thing.buffer)}`; - } - if (thing.byteLength !== thing.buffer.byteLength) { - str2 += `,${thing.startOffset},${thing.byteLength}`; - } - return str2 + ")"; - } - case "ArrayBuffer": { - const ui8 = new Uint8Array(thing); - return `new Uint8Array([${ui8.toString()}]).buffer`; - } - case "Temporal.Duration": - case "Temporal.Instant": - case "Temporal.PlainDate": - case "Temporal.PlainTime": - case "Temporal.PlainDateTime": - case "Temporal.PlainMonthDay": - case "Temporal.PlainYearMonth": - case "Temporal.ZonedDateTime": - return `${type}.from(${stringify_string(thing.toString())})`; - default: - const keys2 = Object.keys(thing); - const obj = keys2.map((key) => `${safe_key(key)}:${stringify(thing[key])}`).join(","); - const proto = Object.getPrototypeOf(thing); - if (proto === null) { - return keys2.length > 0 ? `{${obj},__proto__:null}` : `{__proto__:null}`; - } - return `{${obj}}`; - } - } - const str = stringify(value); - if (names.size) { - const params = []; - const statements = []; - const values = []; - names.forEach((name, thing) => { - params.push(name); - if (custom.has(thing)) { - values.push( - /** @type {string} */ - custom.get(thing) - ); - return; - } - if (is_primitive(thing)) { - values.push(stringify_primitive(thing)); - return; - } - const type = get_type(thing); - switch (type) { - case "Number": - case "String": - case "Boolean": - case "BigInt": - values.push(`Object(${stringify(thing.valueOf())})`); - break; - case "RegExp": - values.push(thing.toString()); - break; - case "Date": - values.push(`new Date(${thing.getTime()})`); - break; - case "URL": - values.push(`new URL(${stringify_string(thing.toString())})`); - break; - case "URLSearchParams": - values.push(`new URLSearchParams(${stringify_string(thing.toString())})`); - break; - case "Array": - values.push(`Array(${thing.length})`); - thing.forEach((v, i) => { - statements.push(`${name}[${i}]=${stringify(v)}`); - }); - break; - case "Set": - values.push(`new Set`); - statements.push( - `${name}.${Array.from(thing).map((v) => `add(${stringify(v)})`).join(".")}` - ); - break; - case "Map": - values.push(`new Map`); - statements.push( - `${name}.${Array.from(thing).map(([k, v]) => `set(${stringify(k)}, ${stringify(v)})`).join(".")}` - ); - break; - case "Int8Array": - case "Uint8Array": - case "Uint8ClampedArray": - case "Int16Array": - case "Uint16Array": - case "Float16Array": - case "Int32Array": - case "Uint32Array": - case "Float32Array": - case "Float64Array": - case "BigInt64Array": - case "BigUint64Array": { - let str2 = `new ${type}`; - if (!names.has(thing.buffer)) { - const array = new thing.constructor(thing.buffer); - str2 += `([${array}])`; - } else { - str2 += `(${stringify(thing.buffer)})`; - } - if (thing.byteLength !== thing.buffer.byteLength) { - const start = thing.byteOffset / thing.BYTES_PER_ELEMENT; - const end = start + thing.length; - str2 += `.subarray(${start},${end})`; - } - values.push(`{}`); - statements.push(`${name}=${str2}`); - break; - } - case "DataView": { - let str2 = `new DataView`; - if (!names.has(thing.buffer)) { - str2 += `(new Uint8Array([${new Uint8Array(thing.buffer)}]).buffer`; - } else { - str2 += `(${stringify(thing.buffer)}`; - } - if (thing.byteLength !== thing.buffer.byteLength) { - str2 += `,${thing.byteOffset},${thing.byteLength}`; - } - str2 += ")"; - values.push(`{}`); - statements.push(`${name}=${str2}`); - break; - } - case "ArrayBuffer": - values.push(`new Uint8Array([${new Uint8Array(thing)}]).buffer`); - break; - default: - values.push(Object.getPrototypeOf(thing) === null ? "Object.create(null)" : "{}"); - Object.keys(thing).forEach((key) => { - statements.push(`${name}${safe_prop(key)}=${stringify(thing[key])}`); - }); - } - }); - statements.push(`return ${str}`); - return `(function(${params.join(",")}){${statements.join(";")}}(${values.join(",")}))`; - } else { - return str; - } -} -function get_name(num) { - let name = ""; - do { - name = chars[num % chars.length] + name; - num = ~~(num / chars.length) - 1; - } while (num >= 0); - return reserved.test(name) ? `${name}0` : name; -} -function escape_unsafe_char(c) { - return escaped[c] || c; -} -function escape_unsafe_chars(str) { - return str.replace(unsafe_chars, escape_unsafe_char); -} -function safe_key(key) { - return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escape_unsafe_chars(JSON.stringify(key)); -} -function safe_prop(key) { - return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? `.${key}` : `[${escape_unsafe_chars(JSON.stringify(key))}]`; -} -function stringify_primitive(thing) { - const type = typeof thing; - if (type === "string") return stringify_string(thing); - if (thing === void 0) return "void 0"; - if (thing === 0 && 1 / thing < 0) return "-0"; - const str = String(thing); - if (type === "number") return str.replace(/^(-)?0\./, "$1."); - if (type === "bigint") return thing + "n"; - return str; -} -function await_invalid() { - const error = new Error(`await_invalid -Encountered asynchronous work while rendering synchronously. -https://svelte.dev/e/await_invalid`); - error.name = "Svelte error"; - throw error; -} -function hydratable_serialization_failed(key, stack) { - const error = new Error(`hydratable_serialization_failed -Failed to serialize \`hydratable\` data for key \`${key}\`. - -\`hydratable\` can serialize anything [\`uneval\` from \`devalue\`](https://npmjs.com/package/uneval) can, plus Promises. - -Cause: -${stack} -https://svelte.dev/e/hydratable_serialization_failed`); - error.name = "Svelte error"; - throw error; -} -function invalid_csp() { - const error = new Error(`invalid_csp -\`csp.nonce\` was set while \`csp.hash\` was \`true\`. These options cannot be used simultaneously. -https://svelte.dev/e/invalid_csp`); - error.name = "Svelte error"; - throw error; -} -function invalid_id_prefix() { - const error = new Error(`invalid_id_prefix -The \`idPrefix\` option cannot include \`--\`. -https://svelte.dev/e/invalid_id_prefix`); - error.name = "Svelte error"; - throw error; -} -function server_context_required() { - const error = new Error(`server_context_required -Could not resolve \`render\` context. -https://svelte.dev/e/server_context_required`); - error.name = "Svelte error"; - throw error; -} -function get_render_context() { - const store = als?.getStore(); - { - server_context_required(); - } - return store; -} -let als = null; -export { - DevalueError as D, - is_plain_object as a, - stringify_string as b, - get_render_context as c, - invalid_csp as d, - enumerable_symbols as e, - await_invalid as f, - get_type as g, - hydratable_serialization_failed as h, - is_primitive as i, - invalid_id_prefix as j, - stringify_key as s, - uneval as u, - valid_array_indices as v -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/renderer.js - -```js -import { t as to_style, p as clsx, q as to_class, a as attr, u as ssr_context, v as set_ssr_context, n as noop, w as push, x as pop, e as escape_html, y as has_own_property } from "./attributes.js"; -import { d as invalid_csp, f as await_invalid, c as get_render_context, j as invalid_id_prefix, u as uneval } from "./render-context.js"; -const DERIVED = 1 << 1; -const EFFECT = 1 << 2; -const RENDER_EFFECT = 1 << 3; -const MANAGED_EFFECT = 1 << 24; -const BLOCK_EFFECT = 1 << 4; -const BRANCH_EFFECT = 1 << 5; -const ROOT_EFFECT = 1 << 6; -const BOUNDARY_EFFECT = 1 << 7; -const CONNECTED = 1 << 9; -const CLEAN = 1 << 10; -const DIRTY = 1 << 11; -const MAYBE_DIRTY = 1 << 12; -const INERT = 1 << 13; -const DESTROYED = 1 << 14; -const REACTION_RAN = 1 << 15; -const DESTROYING = 1 << 25; -const EFFECT_TRANSPARENT = 1 << 16; -const EAGER_EFFECT = 1 << 17; -const HEAD_EFFECT = 1 << 18; -const EFFECT_PRESERVED = 1 << 19; -const USER_EFFECT = 1 << 20; -const WAS_MARKED = 1 << 16; -const REACTION_IS_UPDATING = 1 << 21; -const ASYNC = 1 << 22; -const ERROR_VALUE = 1 << 23; -const STATE_SYMBOL = /* @__PURE__ */ Symbol("$state"); -const LEGACY_PROPS = /* @__PURE__ */ Symbol("legacy props"); -const STALE_REACTION = new class StaleReactionError extends Error { - name = "StaleReactionError"; - message = "The reaction that called `getAbortSignal()` was re-run or destroyed"; -}(); -const COMMENT_NODE = 8; -let controller = null; -function abort() { - controller?.abort(STALE_REACTION); - controller = null; -} -const HYDRATION_START = "["; -const HYDRATION_START_ELSE = "[!"; -const HYDRATION_START_FAILED = "[?"; -const HYDRATION_END = "]"; -const HYDRATION_ERROR = {}; -const ELEMENT_IS_NAMESPACED = 1; -const ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1; -const ELEMENT_IS_INPUT = 1 << 2; -const UNINITIALIZED = /* @__PURE__ */ Symbol(); -function unresolved_hydratable(key, stack) { - { - console.warn(`https://svelte.dev/e/unresolved_hydratable`); - } -} -const BLOCK_OPEN = ``; -const BLOCK_CLOSE = ``; -const EMPTY_COMMENT = ``; -const DOM_BOOLEAN_ATTRIBUTES = [ - "allowfullscreen", - "async", - "autofocus", - "autoplay", - "checked", - "controls", - "default", - "disabled", - "formnovalidate", - "indeterminate", - "inert", - "ismap", - "loop", - "multiple", - "muted", - "nomodule", - "novalidate", - "open", - "playsinline", - "readonly", - "required", - "reversed", - "seamless", - "selected", - "webkitdirectory", - "defer", - "disablepictureinpicture", - "disableremoteplayback" -]; -function is_boolean_attribute(name) { - return DOM_BOOLEAN_ATTRIBUTES.includes(name); -} -const PASSIVE_EVENTS = ["touchstart", "touchmove"]; -function is_passive_event(name) { - return PASSIVE_EVENTS.includes(name); -} -const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; -function render(component, options = {}) { - if (options.csp?.hash && options.csp.nonce) { - invalid_csp(); - } - return Renderer.render( - /** @type {Component} */ - component, - options - ); -} -function head(hash, renderer, fn) { - renderer.head((renderer2) => { - renderer2.push(``); - renderer2.child(fn); - renderer2.push(EMPTY_COMMENT); - }); -} -function attributes(attrs, css_hash, classes, styles, flags = 0) { - if (styles) { - attrs.style = to_style(attrs.style, styles); - } - if (attrs.class) { - attrs.class = clsx(attrs.class); - } - if (css_hash || classes) { - attrs.class = to_class(attrs.class, css_hash, classes); - } - let attr_str = ""; - let name; - const is_html = (flags & ELEMENT_IS_NAMESPACED) === 0; - const lowercase = (flags & ELEMENT_PRESERVE_ATTRIBUTE_CASE) === 0; - const is_input = (flags & ELEMENT_IS_INPUT) !== 0; - for (name of Object.keys(attrs)) { - if (typeof attrs[name] === "function") continue; - if (name[0] === "$" && name[1] === "$") continue; - if (INVALID_ATTR_NAME_CHAR_REGEX.test(name)) continue; - var value = attrs[name]; - var lower = name.toLowerCase(); - if (lowercase) name = lower; - if (lower.length > 2 && lower.startsWith("on")) continue; - if (is_input) { - if (name === "defaultvalue" || name === "defaultchecked") { - name = name === "defaultvalue" ? "value" : "checked"; - if (attrs[name]) continue; - } - } - attr_str += attr(name, value, is_html && is_boolean_attribute(name)); - } - return attr_str; -} -function once(get_value) { - let value = ( - /** @type {V} */ - UNINITIALIZED - ); - return () => { - if (value === UNINITIALIZED) { - value = get_value(); - } - return value; - }; -} -function derived(fn) { - const get_value = ssr_context === null ? fn : once(fn); - let updated_value; - return function(new_value) { - if (arguments.length === 0) { - return updated_value ?? get_value(); - } - updated_value = new_value; - return updated_value; - }; -} -let text_encoder; -let crypto; -const obfuscated_import = (module_name) => import( - /* @vite-ignore */ - module_name -); -async function sha256(data) { - text_encoder ??= new TextEncoder(); - crypto ??= globalThis.crypto?.subtle?.digest ? globalThis.crypto : ( - // @ts-ignore - we don't install node types in the prod build - // don't use import('node:crypto') directly because static analysers will think we rely on node when we don't - (await obfuscated_import("node:crypto")).webcrypto - ); - const hash_buffer = await crypto.subtle.digest("SHA-256", text_encoder.encode(data)); - return base64_encode(hash_buffer); -} -function base64_encode(bytes) { - if (globalThis.Buffer) { - return globalThis.Buffer.from(bytes).toString("base64"); - } - let binary = ""; - for (let i = 0; i < bytes.length; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} -class Renderer { - /** - * The contents of the renderer. - * @type {RendererItem[]} - */ - #out = []; - /** - * Any `onDestroy` callbacks registered during execution of this renderer. - * @type {(() => void)[] | undefined} - */ - #on_destroy = void 0; - /** - * Whether this renderer is a component body. - * @type {boolean} - */ - #is_component_body = false; - /** - * If set, this renderer is an error boundary. When async collection - * of the children fails, the failed snippet is rendered instead. - * @type {{ - * failed: (renderer: Renderer, error: unknown, reset: () => void) => void; - * transformError: (error: unknown) => unknown; - * context: SSRContext | null; - * } | null} - */ - #boundary = null; - /** - * The type of string content that this renderer is accumulating. - * @type {RendererType} - */ - type; - /** @type {Renderer | undefined} */ - #parent; - /** - * Asynchronous work associated with this renderer - * @type {Promise | undefined} - */ - promise = void 0; - /** - * State which is associated with the content tree as a whole. - * It will be re-exposed, uncopied, on all children. - * @type {SSRState} - * @readonly - */ - global; - /** - * State that is local to the branch it is declared in. - * It will be shallow-copied to all children. - * - * @type {{ select_value: string | undefined }} - */ - local; - /** - * @param {SSRState} global - * @param {Renderer | undefined} [parent] - */ - constructor(global, parent) { - this.#parent = parent; - this.global = global; - this.local = parent ? { ...parent.local } : { select_value: void 0 }; - this.type = parent ? parent.type : "body"; - } - /** - * @param {(renderer: Renderer) => void} fn - */ - head(fn) { - const head2 = new Renderer(this.global, this); - head2.type = "head"; - this.#out.push(head2); - head2.child(fn); - } - /** - * @param {Array>} blockers - * @param {(renderer: Renderer) => void} fn - */ - async_block(blockers, fn) { - this.#out.push(BLOCK_OPEN); - this.async(blockers, fn); - this.#out.push(BLOCK_CLOSE); - } - /** - * @param {Array>} blockers - * @param {(renderer: Renderer) => void} fn - */ - async(blockers, fn) { - let callback = fn; - if (blockers.length > 0) { - const context = ssr_context; - callback = (renderer) => { - return Promise.all(blockers).then(() => { - const previous_context = ssr_context; - try { - set_ssr_context(context); - return fn(renderer); - } finally { - set_ssr_context(previous_context); - } - }); - }; - } - this.child(callback); - } - /** - * @param {Array<() => void>} thunks - */ - run(thunks) { - const context = ssr_context; - let promise = Promise.resolve(thunks[0]()); - const promises = [promise]; - for (const fn of thunks.slice(1)) { - promise = promise.then(() => { - const previous_context = ssr_context; - set_ssr_context(context); - try { - return fn(); - } finally { - set_ssr_context(previous_context); - } - }); - promises.push(promise); - } - promise.catch(noop); - this.promise = promise; - return promises; - } - /** - * @param {(renderer: Renderer) => MaybePromise} fn - */ - child_block(fn) { - this.#out.push(BLOCK_OPEN); - this.child(fn); - this.#out.push(BLOCK_CLOSE); - } - /** - * Create a child renderer. The child renderer inherits the state from the parent, - * but has its own content. - * @param {(renderer: Renderer) => MaybePromise} fn - */ - child(fn) { - const child = new Renderer(this.global, this); - this.#out.push(child); - const parent = ssr_context; - set_ssr_context({ - ...ssr_context, - p: parent, - c: null, - r: child - }); - const result = fn(child); - set_ssr_context(parent); - if (result instanceof Promise) { - result.catch(noop); - result.finally(() => set_ssr_context(null)).catch(noop); - if (child.global.mode === "sync") { - await_invalid(); - } - child.promise = result; - } - return child; - } - /** - * Render children inside an error boundary. If the children throw and the API-level - * `transformError` transform handles the error (doesn't re-throw), the `failed` snippet is - * rendered instead. Otherwise the error propagates. - * - * @param {{ failed?: (renderer: Renderer, error: unknown, reset: () => void) => void }} props - * @param {(renderer: Renderer) => MaybePromise} children_fn - */ - boundary(props, children_fn) { - const child = new Renderer(this.global, this); - this.#out.push(child); - const parent_context = ssr_context; - if (props.failed) { - child.#boundary = { - failed: props.failed, - transformError: this.global.transformError, - context: parent_context - }; - } - set_ssr_context({ - ...ssr_context, - p: parent_context, - c: null, - r: child - }); - try { - const result = children_fn(child); - set_ssr_context(parent_context); - if (result instanceof Promise) { - if (child.global.mode === "sync") { - await_invalid(); - } - result.catch(noop); - child.promise = result; - } - } catch (error) { - set_ssr_context(parent_context); - const failed_snippet = props.failed; - if (!failed_snippet) throw error; - const result = this.global.transformError(error); - child.#out.length = 0; - child.#boundary = null; - if (result instanceof Promise) { - if (this.global.mode === "sync") { - await_invalid(); - } - child.promise = /** @type {Promise} */ - result.then((transformed) => { - set_ssr_context(parent_context); - child.#out.push(Renderer.#serialize_failed_boundary(transformed)); - failed_snippet(child, transformed, noop); - child.#out.push(BLOCK_CLOSE); - }); - child.promise.catch(noop); - } else { - child.#out.push(Renderer.#serialize_failed_boundary(result)); - failed_snippet(child, result, noop); - child.#out.push(BLOCK_CLOSE); - } - } - } - /** - * Create a component renderer. The component renderer inherits the state from the parent, - * but has its own content. It is treated as an ordering boundary for ondestroy callbacks. - * @param {(renderer: Renderer) => MaybePromise} fn - * @param {Function} [component_fn] - * @returns {void} - */ - component(fn, component_fn) { - push(); - const child = this.child(fn); - child.#is_component_body = true; - pop(); - } - /** - * @param {Record} attrs - * @param {(renderer: Renderer) => void} fn - * @param {string | undefined} [css_hash] - * @param {Record | undefined} [classes] - * @param {Record | undefined} [styles] - * @param {number | undefined} [flags] - * @param {boolean | undefined} [is_rich] - * @returns {void} - */ - select(attrs, fn, css_hash, classes, styles, flags, is_rich) { - const { value, ...select_attrs } = attrs; - this.push(``); - this.child((renderer) => { - renderer.local.select_value = value; - fn(renderer); - }); - this.push(`${is_rich ? "" : ""}`); - } - /** - * @param {Record} attrs - * @param {string | number | boolean | ((renderer: Renderer) => void)} body - * @param {string | undefined} [css_hash] - * @param {Record | undefined} [classes] - * @param {Record | undefined} [styles] - * @param {number | undefined} [flags] - * @param {boolean | undefined} [is_rich] - */ - option(attrs, body, css_hash, classes, styles, flags, is_rich) { - this.#out.push(` { - if (has_own_property.call(attrs, "value")) { - value = attrs.value; - } - if (value === this.local.select_value) { - renderer.#out.push(' selected=""'); - } - renderer.#out.push(`>${body2}${is_rich ? "" : ""}`); - if (head2) { - renderer.head((child) => child.push(head2)); - } - }; - if (typeof body === "function") { - this.child((renderer) => { - const r = new Renderer(this.global, this); - body(r); - if (this.global.mode === "async") { - return r.#collect_content_async().then((content) => { - close(renderer, content.body.replaceAll("", ""), content); - }); - } else { - const content = r.#collect_content(); - close(renderer, content.body.replaceAll("", ""), content); - } - }); - } else { - close(this, body, { body: escape_html(body) }); - } - } - /** - * @param {(renderer: Renderer) => void} fn - */ - title(fn) { - const path = this.get_path(); - const close = (head2) => { - this.global.set_title(head2, path); - }; - this.child((renderer) => { - const r = new Renderer(renderer.global, renderer); - fn(r); - if (renderer.global.mode === "async") { - return r.#collect_content_async().then((content) => { - close(content.head); - }); - } else { - const content = r.#collect_content(); - close(content.head); - } - }); - } - /** - * @param {string | (() => Promise)} content - */ - push(content) { - if (typeof content === "function") { - this.child(async (renderer) => renderer.push(await content())); - } else { - this.#out.push(content); - } - } - /** - * @param {() => void} fn - */ - on_destroy(fn) { - (this.#on_destroy ??= []).push(fn); - } - /** - * @returns {number[]} - */ - get_path() { - return this.#parent ? [...this.#parent.get_path(), this.#parent.#out.indexOf(this)] : []; - } - /** - * @deprecated this is needed for legacy component bindings - */ - copy() { - const copy = new Renderer(this.global, this.#parent); - copy.#out = this.#out.map((item) => item instanceof Renderer ? item.copy() : item); - copy.promise = this.promise; - return copy; - } - /** - * @param {Renderer} other - * @deprecated this is needed for legacy component bindings - */ - subsume(other) { - if (this.global.mode !== other.global.mode) { - throw new Error( - "invariant: A renderer cannot switch modes. If you're seeing this, there's a compiler bug. File an issue!" - ); - } - this.local = other.local; - this.#out = other.#out.map((item, i) => { - const current = this.#out[i]; - if (current instanceof Renderer && item instanceof Renderer) { - current.subsume(item); - return current; - } - return item; - }); - this.promise = other.promise; - this.type = other.type; - } - get length() { - return this.#out.length; - } - /** - * Creates the hydration comment that marks the start of a failed boundary. - * The error is JSON-serialized and embedded inside an HTML comment for the client - * to parse during hydration. The JSON is escaped to prevent `-->` or ``; - } - /** - * Only available on the server and when compiling with the `server` option. - * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. - * @template {Record} Props - * @param {Component} component - * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} [options] - * @returns {RenderOutput} - */ - static render(component, options = {}) { - let sync; - const result = ( - /** @type {RenderOutput} */ - {} - ); - Object.defineProperties(result, { - html: { - get: () => { - return (sync ??= Renderer.#render(component, options)).body; - } - }, - head: { - get: () => { - return (sync ??= Renderer.#render(component, options)).head; - } - }, - body: { - get: () => { - return (sync ??= Renderer.#render(component, options)).body; - } - }, - hashes: { - value: { - script: "" - } - }, - then: { - value: ( - /** - * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. - * - * @template TResult1 - * @template [TResult2=never] - * @param { (value: SyncRenderOutput) => TResult1 } onfulfilled - * @param { (reason: unknown) => TResult2 } onrejected - */ - (onfulfilled, onrejected) => { - { - const result2 = sync ??= Renderer.#render(component, options); - const user_result = onfulfilled({ - head: result2.head, - body: result2.body, - html: result2.body, - hashes: { script: [] } - }); - return Promise.resolve(user_result); - } - } - ) - } - }); - return result; - } - /** - * Collect all of the `onDestroy` callbacks registered during rendering. In an async context, this is only safe to call - * after awaiting `collect_async`. - * - * Child renderers are "porous" and don't affect execution order, but component body renderers - * create ordering boundaries. Within a renderer, callbacks run in order until hitting a component boundary. - * @returns {Iterable<() => void>} - */ - *#collect_on_destroy() { - for (const component of this.#traverse_components()) { - yield* component.#collect_ondestroy(); - } - } - /** - * Performs a depth-first search of renderers, yielding the deepest components first, then additional components as we backtrack up the tree. - * @returns {Iterable} - */ - *#traverse_components() { - for (const child of this.#out) { - if (typeof child !== "string") { - yield* child.#traverse_components(); - } - } - if (this.#is_component_body) { - yield this; - } - } - /** - * @returns {Iterable<() => void>} - */ - *#collect_ondestroy() { - if (this.#on_destroy) { - for (const fn of this.#on_destroy) { - yield fn; - } - } - for (const child of this.#out) { - if (child instanceof Renderer && !child.#is_component_body) { - yield* child.#collect_ondestroy(); - } - } - } - /** - * Render a component. Throws if any of the children are performing asynchronous work. - * - * @template {Record} Props - * @param {Component} component - * @param {{ props?: Omit; context?: Map; idPrefix?: string }} options - * @returns {AccumulatedContent} - */ - static #render(component, options) { - var previous_context = ssr_context; - try { - const renderer = Renderer.#open_render("sync", component, options); - const content = renderer.#collect_content(); - return Renderer.#close_render(content, renderer); - } finally { - abort(); - set_ssr_context(previous_context); - } - } - /** - * Render a component. - * - * @template {Record} Props - * @param {Component} component - * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp }} options - * @returns {Promise} - */ - static async #render_async(component, options) { - const previous_context = ssr_context; - try { - const renderer = Renderer.#open_render("async", component, options); - const content = await renderer.#collect_content_async(); - const hydratables = await renderer.#collect_hydratables(); - if (hydratables !== null) { - content.head = hydratables + content.head; - } - return Renderer.#close_render(content, renderer); - } finally { - set_ssr_context(previous_context); - abort(); - } - } - /** - * Collect all of the code from the `out` array and return it as a string, or a promise resolving to a string. - * @param {AccumulatedContent} content - * @returns {AccumulatedContent} - */ - #collect_content(content = { head: "", body: "" }) { - for (const item of this.#out) { - if (typeof item === "string") { - content[this.type] += item; - } else if (item instanceof Renderer) { - item.#collect_content(content); - } - } - return content; - } - /** - * Collect all of the code from the `out` array and return it as a string. - * @param {AccumulatedContent} content - * @returns {Promise} - */ - async #collect_content_async(content = { head: "", body: "" }) { - await this.promise; - for (const item of this.#out) { - if (typeof item === "string") { - content[this.type] += item; - } else if (item instanceof Renderer) { - if (item.#boundary) { - const boundary_content = { head: "", body: "" }; - try { - await item.#collect_content_async(boundary_content); - content.head += boundary_content.head; - content.body += boundary_content.body; - } catch (error) { - const { context, failed, transformError } = item.#boundary; - set_ssr_context(context); - let transformed = await transformError(error); - const failed_renderer = new Renderer(item.global, item); - failed_renderer.type = item.type; - failed_renderer.#out.push(Renderer.#serialize_failed_boundary(transformed)); - failed(failed_renderer, transformed, noop); - failed_renderer.#out.push(BLOCK_CLOSE); - await failed_renderer.#collect_content_async(content); - } - } else { - await item.#collect_content_async(content); - } - } - } - return content; - } - async #collect_hydratables() { - const ctx = get_render_context().hydratable; - for (const [_, key] of ctx.unresolved_promises) { - unresolved_hydratable(key, ctx.lookup.get(key)?.stack ?? ""); - } - for (const comparison of ctx.comparisons) { - await comparison; - } - return await this.#hydratable_block(ctx); - } - /** - * @template {Record} Props - * @param {'sync' | 'async'} mode - * @param {import('svelte').Component} component - * @param {{ props?: Omit; context?: Map; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options - * @returns {Renderer} - */ - static #open_render(mode, component, options) { - if (options.idPrefix?.includes("--")) { - invalid_id_prefix(); - } - var previous_context = ssr_context; - try { - const renderer = new Renderer( - new SSRState( - mode, - options.idPrefix ? options.idPrefix + "-" : "", - options.csp, - options.transformError - ) - ); - const context = { p: null, c: options.context ?? null, r: renderer }; - set_ssr_context(context); - renderer.push(BLOCK_OPEN); - component(renderer, options.props ?? {}); - renderer.push(BLOCK_CLOSE); - return renderer; - } finally { - set_ssr_context(previous_context); - } - } - /** - * @param {AccumulatedContent} content - * @param {Renderer} renderer - * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }} - */ - static #close_render(content, renderer) { - for (const cleanup of renderer.#collect_on_destroy()) { - cleanup(); - } - let head2 = content.head + renderer.global.get_title(); - let body = content.body; - for (const { hash, code } of renderer.global.css) { - head2 += ``; - } - return { - head: head2, - body, - hashes: { - script: renderer.global.csp.script_hashes - } - }; - } - /** - * @param {HydratableContext} ctx - */ - async #hydratable_block(ctx) { - if (ctx.lookup.size === 0) { - return null; - } - let entries = []; - let has_promises = false; - for (const [k, v] of ctx.lookup) { - if (v.promises) { - has_promises = true; - for (const p of v.promises) await p; - } - entries.push(`[${uneval(k)},${v.serialized}]`); - } - let prelude = `const h = (window.__svelte ??= {}).h ??= new Map();`; - if (has_promises) { - prelude = `const r = (v) => Promise.resolve(v); - ${prelude}`; - } - const body = ` - { - ${prelude} - - for (const [k, v] of [ - ${entries.join(",\n ")} - ]) { - h.set(k, v); - } - } - `; - let csp_attr = ""; - if (this.global.csp.nonce) { - csp_attr = ` nonce="${this.global.csp.nonce}"`; - } else if (this.global.csp.hash) { - const hash = await sha256(body); - this.global.csp.script_hashes.push(`sha256-${hash}`); - } - return ` - ${body}<\/script>`; - } -} -class SSRState { - /** @readonly @type {Csp & { script_hashes: Sha256Source[] }} */ - csp; - /** @readonly @type {'sync' | 'async'} */ - mode; - /** @readonly @type {() => string} */ - uid; - /** @readonly @type {Set<{ hash: string; code: string }>} */ - css = /* @__PURE__ */ new Set(); - /** - * `transformError` passed to `render`. Called when an error boundary catches an error. - * Throws by default if unset in `render`. - * @type {(error: unknown) => unknown} - */ - transformError; - /** @type {{ path: number[], value: string }} */ - #title = { path: [], value: "" }; - /** - * @param {'sync' | 'async'} mode - * @param {string} id_prefix - * @param {Csp} csp - * @param {((error: unknown) => unknown) | undefined} [transformError] - */ - constructor(mode, id_prefix = "", csp = { hash: false }, transformError) { - this.mode = mode; - this.csp = { ...csp, script_hashes: [] }; - this.transformError = transformError ?? ((error) => { - throw error; - }); - let uid = 1; - this.uid = () => `${id_prefix}s${uid++}`; - } - get_title() { - return this.#title.value; - } - /** - * Performs a depth-first (lexicographic) comparison using the path. Rejects sets - * from earlier than or equal to the current value. - * @param {string} value - * @param {number[]} path - */ - set_title(value, path) { - const current = this.#title.path; - let i = 0; - let l = Math.min(path.length, current.length); - while (i < l && path[i] === current[i]) i += 1; - if (path[i] === void 0) return; - if (current[i] === void 0 || path[i] > current[i]) { - this.#title.path = path; - this.#title.value = value; - } - } -} -export { - ASYNC as A, - BOUNDARY_EFFECT as B, - COMMENT_NODE as C, - DIRTY as D, - ERROR_VALUE as E, - HYDRATION_ERROR as H, - INERT as I, - LEGACY_PROPS as L, - MAYBE_DIRTY as M, - REACTION_RAN as R, - STALE_REACTION as S, - UNINITIALIZED as U, - WAS_MARKED as W, - HYDRATION_END as a, - HYDRATION_START as b, - HYDRATION_START_ELSE as c, - EFFECT as d, - CONNECTED as e, - CLEAN as f, - DERIVED as g, - head as h, - BLOCK_EFFECT as i, - DESTROYED as j, - EAGER_EFFECT as k, - RENDER_EFFECT as l, - MANAGED_EFFECT as m, - ROOT_EFFECT as n, - BRANCH_EFFECT as o, - HYDRATION_START_FAILED as p, - EFFECT_TRANSPARENT as q, - EFFECT_PRESERVED as r, - STATE_SYMBOL as s, - HEAD_EFFECT as t, - DESTROYING as u, - USER_EFFECT as v, - REACTION_IS_UPDATING as w, - is_passive_event as x, - render as y, - derived as z -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/root.js - -```js -import { H as HYDRATION_ERROR, C as COMMENT_NODE, a as HYDRATION_END, b as HYDRATION_START, c as HYDRATION_START_ELSE, B as BOUNDARY_EFFECT, R as REACTION_RAN, E as ERROR_VALUE, d as EFFECT, e as CONNECTED, f as CLEAN, M as MAYBE_DIRTY, D as DIRTY, g as DERIVED, W as WAS_MARKED, I as INERT, i as BLOCK_EFFECT, U as UNINITIALIZED, A as ASYNC, j as DESTROYED, k as EAGER_EFFECT, l as RENDER_EFFECT, m as MANAGED_EFFECT, n as ROOT_EFFECT, o as BRANCH_EFFECT, p as HYDRATION_START_FAILED, q as EFFECT_TRANSPARENT, r as EFFECT_PRESERVED, S as STALE_REACTION, s as STATE_SYMBOL, t as HEAD_EFFECT, u as DESTROYING, v as USER_EFFECT, w as REACTION_IS_UPDATING, x as is_passive_event, L as LEGACY_PROPS, y as render, z as derived } from "./renderer.js"; -import { B as BROWSER } from "./errors.js"; -import { r as run_all, d as deferred, i as includes, n as noop, o as object_prototype, b as array_prototype, c as get_descriptor, f as get_prototype_of, h as is_array, j as is_extensible, k as index_of, l as define_property, m as array_from, s as setContext } from "./attributes.js"; -let tracing_mode_flag = false; -function effect_update_depth_exceeded() { - { - throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`); - } -} -function hydration_failed() { - { - throw new Error(`https://svelte.dev/e/hydration_failed`); - } -} -function state_descriptors_fixed() { - { - throw new Error(`https://svelte.dev/e/state_descriptors_fixed`); - } -} -function state_prototype_fixed() { - { - throw new Error(`https://svelte.dev/e/state_prototype_fixed`); - } -} -function state_unsafe_mutation() { - { - throw new Error(`https://svelte.dev/e/state_unsafe_mutation`); - } -} -function svelte_boundary_reset_onerror() { - { - throw new Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`); - } -} -function hydration_mismatch(location) { - { - console.warn(`https://svelte.dev/e/hydration_mismatch`); - } -} -function svelte_boundary_reset_noop() { - { - console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`); - } -} -let hydrating = false; -function set_hydrating(value) { - hydrating = value; -} -let hydrate_node; -function set_hydrate_node(node) { - if (node === null) { - hydration_mismatch(); - throw HYDRATION_ERROR; - } - return hydrate_node = node; -} -function hydrate_next() { - return set_hydrate_node(/* @__PURE__ */ get_next_sibling(hydrate_node)); -} -function next(count = 1) { - if (hydrating) { - var i = count; - var node = hydrate_node; - while (i--) { - node = /** @type {TemplateNode} */ - /* @__PURE__ */ get_next_sibling(node); - } - hydrate_node = node; - } -} -function skip_nodes(remove = true) { - var depth = 0; - var node = hydrate_node; - while (true) { - if (node.nodeType === COMMENT_NODE) { - var data = ( - /** @type {Comment} */ - node.data - ); - if (data === HYDRATION_END) { - if (depth === 0) return node; - depth -= 1; - } else if (data === HYDRATION_START || data === HYDRATION_START_ELSE || // "[1", "[2", etc. for if blocks - data[0] === "[" && !isNaN(Number(data.slice(1)))) { - depth += 1; - } - } - var next2 = ( - /** @type {TemplateNode} */ - /* @__PURE__ */ get_next_sibling(node) - ); - if (remove) node.remove(); - node = next2; - } -} -function equals(value) { - return value === this.v; -} -function safe_not_equal(a, b) { - return a != a ? b == b : a !== b || a !== null && typeof a === "object" || typeof a === "function"; -} -function safe_equals(value) { - return !safe_not_equal(value, this.v); -} -let component_context = null; -function set_component_context(context) { - component_context = context; -} -function push(props, runes = false, fn) { - component_context = { - p: component_context, - i: false, - c: null, - e: null, - s: props, - x: null, - r: ( - /** @type {Effect} */ - active_effect - ), - l: null - }; -} -function pop(component) { - var context = ( - /** @type {ComponentContext} */ - component_context - ); - var effects = context.e; - if (effects !== null) { - context.e = null; - for (var fn of effects) { - create_user_effect(fn); - } - } - context.i = true; - component_context = context.p; - return ( - /** @type {T} */ - {} - ); -} -function is_runes() { - return true; -} -let micro_tasks = []; -function run_micro_tasks() { - var tasks = micro_tasks; - micro_tasks = []; - run_all(tasks); -} -function queue_micro_task(fn) { - if (micro_tasks.length === 0 && !is_flushing_sync) { - var tasks = micro_tasks; - queueMicrotask(() => { - if (tasks === micro_tasks) run_micro_tasks(); - }); - } - micro_tasks.push(fn); -} -function flush_tasks() { - while (micro_tasks.length > 0) { - run_micro_tasks(); - } -} -function handle_error(error) { - var effect = active_effect; - if (effect === null) { - active_reaction.f |= ERROR_VALUE; - return error; - } - if ((effect.f & REACTION_RAN) === 0 && (effect.f & EFFECT) === 0) { - throw error; - } - invoke_error_boundary(error, effect); -} -function invoke_error_boundary(error, effect) { - while (effect !== null) { - if ((effect.f & BOUNDARY_EFFECT) !== 0) { - if ((effect.f & REACTION_RAN) === 0) { - throw error; - } - try { - effect.b.error(error); - return; - } catch (e) { - error = e; - } - } - effect = effect.parent; - } - throw error; -} -const STATUS_MASK = -7169; -function set_signal_status(signal, status) { - signal.f = signal.f & STATUS_MASK | status; -} -function update_derived_status(derived2) { - if ((derived2.f & CONNECTED) !== 0 || derived2.deps === null) { - set_signal_status(derived2, CLEAN); - } else { - set_signal_status(derived2, MAYBE_DIRTY); - } -} -function clear_marked(deps) { - if (deps === null) return; - for (const dep of deps) { - if ((dep.f & DERIVED) === 0 || (dep.f & WAS_MARKED) === 0) { - continue; - } - dep.f ^= WAS_MARKED; - clear_marked( - /** @type {Derived} */ - dep.deps - ); - } -} -function defer_effect(effect, dirty_effects, maybe_dirty_effects) { - if ((effect.f & DIRTY) !== 0) { - dirty_effects.add(effect); - } else if ((effect.f & MAYBE_DIRTY) !== 0) { - maybe_dirty_effects.add(effect); - } - clear_marked(effect.deps); - set_signal_status(effect, CLEAN); -} -const batches = /* @__PURE__ */ new Set(); -let current_batch = null; -let batch_values = null; -let last_scheduled_effect = null; -let is_flushing_sync = false; -let is_processing = false; -let collected_effects = null; -let legacy_updates = null; -var flush_count = 0; -let uid = 1; -class Batch { - id = uid++; - /** - * The current values of any signals that are updated in this batch. - * Tuple format: [value, is_derived] (note: is_derived is false for deriveds, too, if they were overridden via assignment) - * They keys of this map are identical to `this.#previous` - * @type {Map} - */ - current = /* @__PURE__ */ new Map(); - /** - * The values of any signals (sources and deriveds) that are updated in this batch _before_ those updates took place. - * They keys of this map are identical to `this.#current` - * @type {Map} - */ - previous = /* @__PURE__ */ new Map(); - /** - * When the batch is committed (and the DOM is updated), we need to remove old branches - * and append new ones by calling the functions added inside (if/each/key/etc) blocks - * @type {Set<(batch: Batch) => void>} - */ - #commit_callbacks = /* @__PURE__ */ new Set(); - /** - * If a fork is discarded, we need to destroy any effects that are no longer needed - * @type {Set<(batch: Batch) => void>} - */ - #discard_callbacks = /* @__PURE__ */ new Set(); - /** - * Async effects that are currently in flight - * @type {Map} - */ - #pending = /* @__PURE__ */ new Map(); - /** - * Async effects that are currently in flight, _not_ inside a pending boundary - * @type {Map} - */ - #blocking_pending = /* @__PURE__ */ new Map(); - /** - * A deferred that resolves when the batch is committed, used with `settled()` - * TODO replace with Promise.withResolvers once supported widely enough - * @type {{ promise: Promise, resolve: (value?: any) => void, reject: (reason: unknown) => void } | null} - */ - #deferred = null; - /** - * The root effects that need to be flushed - * @type {Effect[]} - */ - #roots = []; - /** - * Effects created while this batch was active. - * @type {Effect[]} - */ - #new_effects = []; - /** - * Deferred effects (which run after async work has completed) that are DIRTY - * @type {Set} - */ - #dirty_effects = /* @__PURE__ */ new Set(); - /** - * Deferred effects that are MAYBE_DIRTY - * @type {Set} - */ - #maybe_dirty_effects = /* @__PURE__ */ new Set(); - /** - * A map of branches that still exist, but will be destroyed when this batch - * is committed — we skip over these during `process`. - * The value contains child effects that were dirty/maybe_dirty before being reset, - * so they can be rescheduled if the branch survives. - * @type {Map} - */ - #skipped_branches = /* @__PURE__ */ new Map(); - /** - * Inverse of #skipped_branches which we need to tell prior batches to unskip them when committing - * @type {Set} - */ - #unskipped_branches = /* @__PURE__ */ new Set(); - is_fork = false; - #decrement_queued = false; - /** @type {Set} */ - #blockers = /* @__PURE__ */ new Set(); - #is_deferred() { - return this.is_fork || this.#blocking_pending.size > 0; - } - #is_blocked() { - for (const batch of this.#blockers) { - for (const effect of batch.#blocking_pending.keys()) { - var skipped = false; - var e = effect; - while (e.parent !== null) { - if (this.#skipped_branches.has(e)) { - skipped = true; - break; - } - e = e.parent; - } - if (!skipped) { - return true; - } - } - } - return false; - } - /** - * Add an effect to the #skipped_branches map and reset its children - * @param {Effect} effect - */ - skip_effect(effect) { - if (!this.#skipped_branches.has(effect)) { - this.#skipped_branches.set(effect, { d: [], m: [] }); - } - this.#unskipped_branches.delete(effect); - } - /** - * Remove an effect from the #skipped_branches map and reschedule - * any tracked dirty/maybe_dirty child effects - * @param {Effect} effect - * @param {(e: Effect) => void} callback - */ - unskip_effect(effect, callback = (e) => this.schedule(e)) { - var tracked = this.#skipped_branches.get(effect); - if (tracked) { - this.#skipped_branches.delete(effect); - for (var e of tracked.d) { - set_signal_status(e, DIRTY); - callback(e); - } - for (e of tracked.m) { - set_signal_status(e, MAYBE_DIRTY); - callback(e); - } - } - this.#unskipped_branches.add(effect); - } - #process() { - if (flush_count++ > 1e3) { - batches.delete(this); - infinite_loop_guard(); - } - if (!this.#is_deferred()) { - for (const e of this.#dirty_effects) { - this.#maybe_dirty_effects.delete(e); - set_signal_status(e, DIRTY); - this.schedule(e); - } - for (const e of this.#maybe_dirty_effects) { - set_signal_status(e, MAYBE_DIRTY); - this.schedule(e); - } - } - const roots = this.#roots; - this.#roots = []; - this.apply(); - var effects = collected_effects = []; - var render_effects = []; - var updates = legacy_updates = []; - for (const root2 of roots) { - try { - this.#traverse(root2, effects, render_effects); - } catch (e) { - reset_all(root2); - throw e; - } - } - current_batch = null; - if (updates.length > 0) { - var batch = Batch.ensure(); - for (const e of updates) { - batch.schedule(e); - } - } - collected_effects = null; - legacy_updates = null; - if (this.#is_deferred() || this.#is_blocked()) { - this.#defer_effects(render_effects); - this.#defer_effects(effects); - for (const [e, t] of this.#skipped_branches) { - reset_branch(e, t); - } - } else { - if (this.#pending.size === 0) { - batches.delete(this); - } - this.#dirty_effects.clear(); - this.#maybe_dirty_effects.clear(); - for (const fn of this.#commit_callbacks) fn(this); - this.#commit_callbacks.clear(); - flush_queued_effects(render_effects); - flush_queued_effects(effects); - this.#deferred?.resolve(); - } - var next_batch = ( - /** @type {Batch | null} */ - /** @type {unknown} */ - current_batch - ); - if (this.#roots.length > 0) { - const batch2 = next_batch ??= this; - batch2.#roots.push(...this.#roots.filter((r) => !batch2.#roots.includes(r))); - } - if (next_batch !== null) { - batches.add(next_batch); - next_batch.#process(); - } - } - /** - * Traverse the effect tree, executing effects or stashing - * them for later execution as appropriate - * @param {Effect} root - * @param {Effect[]} effects - * @param {Effect[]} render_effects - */ - #traverse(root2, effects, render_effects) { - root2.f ^= CLEAN; - var effect = root2.first; - while (effect !== null) { - var flags2 = effect.f; - var is_branch = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0; - var is_skippable_branch = is_branch && (flags2 & CLEAN) !== 0; - var skip = is_skippable_branch || (flags2 & INERT) !== 0 || this.#skipped_branches.has(effect); - if (!skip && effect.fn !== null) { - if (is_branch) { - effect.f ^= CLEAN; - } else if ((flags2 & EFFECT) !== 0) { - effects.push(effect); - } else if (is_dirty(effect)) { - if ((flags2 & BLOCK_EFFECT) !== 0) this.#maybe_dirty_effects.add(effect); - update_effect(effect); - } - var child = effect.first; - if (child !== null) { - effect = child; - continue; - } - } - while (effect !== null) { - var next2 = effect.next; - if (next2 !== null) { - effect = next2; - break; - } - effect = effect.parent; - } - } - } - /** - * @param {Effect[]} effects - */ - #defer_effects(effects) { - for (var i = 0; i < effects.length; i += 1) { - defer_effect(effects[i], this.#dirty_effects, this.#maybe_dirty_effects); - } - } - /** - * Associate a change to a given source with the current - * batch, noting its previous and current values - * @param {Value} source - * @param {any} value - * @param {boolean} [is_derived] - */ - capture(source2, value, is_derived = false) { - if (source2.v !== UNINITIALIZED && !this.previous.has(source2)) { - this.previous.set(source2, source2.v); - } - if ((source2.f & ERROR_VALUE) === 0) { - this.current.set(source2, [value, is_derived]); - batch_values?.set(source2, value); - } - if (!this.is_fork) { - source2.v = value; - } - } - activate() { - current_batch = this; - } - deactivate() { - current_batch = null; - batch_values = null; - } - flush() { - try { - is_processing = true; - current_batch = this; - this.#process(); - } finally { - flush_count = 0; - last_scheduled_effect = null; - collected_effects = null; - legacy_updates = null; - is_processing = false; - current_batch = null; - batch_values = null; - old_values.clear(); - } - } - discard() { - for (const fn of this.#discard_callbacks) fn(this); - this.#discard_callbacks.clear(); - batches.delete(this); - } - /** - * @param {Effect} effect - */ - register_created_effect(effect) { - this.#new_effects.push(effect); - } - #commit() { - for (const batch of batches) { - var is_earlier = batch.id < this.id; - var sources = []; - for (const [source3, [value, is_derived]] of this.current) { - if (batch.current.has(source3)) { - var batch_value = ( - /** @type {[any, boolean]} */ - batch.current.get(source3)[0] - ); - if (is_earlier && value !== batch_value) { - batch.current.set(source3, [value, is_derived]); - } else { - continue; - } - } - sources.push(source3); - } - var others = [...batch.current.keys()].filter((s) => !this.current.has(s)); - if (others.length === 0) { - if (is_earlier) { - batch.discard(); - } - } else if (sources.length > 0) { - if (is_earlier) { - for (const unskipped of this.#unskipped_branches) { - batch.unskip_effect(unskipped, (e) => { - if ((e.f & (BLOCK_EFFECT | ASYNC)) !== 0) { - batch.schedule(e); - } else { - batch.#defer_effects([e]); - } - }); - } - } - batch.activate(); - var marked = /* @__PURE__ */ new Set(); - var checked = /* @__PURE__ */ new Map(); - for (var source2 of sources) { - mark_effects(source2, others, marked, checked); - } - checked = /* @__PURE__ */ new Map(); - var current_unequal = [...batch.current.keys()].filter( - (c) => this.current.has(c) ? ( - /** @type {[any, boolean]} */ - this.current.get(c)[0] !== c - ) : true - ); - for (const effect of this.#new_effects) { - if ((effect.f & (DESTROYED | INERT | EAGER_EFFECT)) === 0 && depends_on(effect, current_unequal, checked)) { - if ((effect.f & (ASYNC | BLOCK_EFFECT)) !== 0) { - set_signal_status(effect, DIRTY); - batch.schedule(effect); - } else { - batch.#dirty_effects.add(effect); - } - } - } - if (batch.#roots.length > 0) { - batch.apply(); - for (var root2 of batch.#roots) { - batch.#traverse(root2, [], []); - } - batch.#roots = []; - } - batch.deactivate(); - } - } - for (const batch of batches) { - if (batch.#blockers.has(this)) { - batch.#blockers.delete(this); - if (batch.#blockers.size === 0 && !batch.#is_deferred()) { - batch.activate(); - batch.#process(); - } - } - } - } - /** - * @param {boolean} blocking - * @param {Effect} effect - */ - increment(blocking, effect) { - let pending_count = this.#pending.get(effect) ?? 0; - this.#pending.set(effect, pending_count + 1); - if (blocking) { - let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0; - this.#blocking_pending.set(effect, blocking_pending_count + 1); - } - } - /** - * @param {boolean} blocking - * @param {Effect} effect - * @param {boolean} skip - whether to skip updates (because this is triggered by a stale reaction) - */ - decrement(blocking, effect, skip) { - let pending_count = this.#pending.get(effect) ?? 0; - if (pending_count === 1) { - this.#pending.delete(effect); - } else { - this.#pending.set(effect, pending_count - 1); - } - if (blocking) { - let blocking_pending_count = this.#blocking_pending.get(effect) ?? 0; - if (blocking_pending_count === 1) { - this.#blocking_pending.delete(effect); - } else { - this.#blocking_pending.set(effect, blocking_pending_count - 1); - } - } - if (this.#decrement_queued || skip) return; - this.#decrement_queued = true; - queue_micro_task(() => { - this.#decrement_queued = false; - this.flush(); - }); - } - /** - * @param {Set} dirty_effects - * @param {Set} maybe_dirty_effects - */ - transfer_effects(dirty_effects, maybe_dirty_effects) { - for (const e of dirty_effects) { - this.#dirty_effects.add(e); - } - for (const e of maybe_dirty_effects) { - this.#maybe_dirty_effects.add(e); - } - dirty_effects.clear(); - maybe_dirty_effects.clear(); - } - /** @param {(batch: Batch) => void} fn */ - oncommit(fn) { - this.#commit_callbacks.add(fn); - } - /** @param {(batch: Batch) => void} fn */ - ondiscard(fn) { - this.#discard_callbacks.add(fn); - } - settled() { - return (this.#deferred ??= deferred()).promise; - } - static ensure() { - if (current_batch === null) { - const batch = current_batch = new Batch(); - if (!is_processing) { - batches.add(current_batch); - if (!is_flushing_sync) { - queue_micro_task(() => { - if (current_batch !== batch) { - return; - } - batch.flush(); - }); - } - } - } - return current_batch; - } - apply() { - { - batch_values = null; - return; - } - } - /** - * - * @param {Effect} effect - */ - schedule(effect) { - last_scheduled_effect = effect; - if (effect.b?.is_pending && (effect.f & (EFFECT | RENDER_EFFECT | MANAGED_EFFECT)) !== 0 && (effect.f & REACTION_RAN) === 0) { - effect.b.defer_effect(effect); - return; - } - var e = effect; - while (e.parent !== null) { - e = e.parent; - var flags2 = e.f; - if (collected_effects !== null && e === active_effect) { - if ((active_reaction === null || (active_reaction.f & DERIVED) === 0) && true) { - return; - } - } - if ((flags2 & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) { - if ((flags2 & CLEAN) === 0) { - return; - } - e.f ^= CLEAN; - } - } - this.#roots.push(e); - } -} -function flushSync(fn) { - var was_flushing_sync = is_flushing_sync; - is_flushing_sync = true; - try { - var result; - if (fn) ; - while (true) { - flush_tasks(); - if (current_batch === null) { - return ( - /** @type {T} */ - result - ); - } - current_batch.flush(); - } - } finally { - is_flushing_sync = was_flushing_sync; - } -} -function infinite_loop_guard() { - try { - effect_update_depth_exceeded(); - } catch (error) { - invoke_error_boundary(error, last_scheduled_effect); - } -} -let eager_block_effects = null; -function flush_queued_effects(effects) { - var length = effects.length; - if (length === 0) return; - var i = 0; - while (i < length) { - var effect = effects[i++]; - if ((effect.f & (DESTROYED | INERT)) === 0 && is_dirty(effect)) { - eager_block_effects = /* @__PURE__ */ new Set(); - update_effect(effect); - if (effect.deps === null && effect.first === null && effect.nodes === null && effect.teardown === null && effect.ac === null) { - unlink_effect(effect); - } - if (eager_block_effects?.size > 0) { - old_values.clear(); - for (const e of eager_block_effects) { - if ((e.f & (DESTROYED | INERT)) !== 0) continue; - const ordered_effects = [e]; - let ancestor = e.parent; - while (ancestor !== null) { - if (eager_block_effects.has(ancestor)) { - eager_block_effects.delete(ancestor); - ordered_effects.push(ancestor); - } - ancestor = ancestor.parent; - } - for (let j = ordered_effects.length - 1; j >= 0; j--) { - const e2 = ordered_effects[j]; - if ((e2.f & (DESTROYED | INERT)) !== 0) continue; - update_effect(e2); - } - } - eager_block_effects.clear(); - } - } - } - eager_block_effects = null; -} -function mark_effects(value, sources, marked, checked) { - if (marked.has(value)) return; - marked.add(value); - if (value.reactions !== null) { - for (const reaction of value.reactions) { - const flags2 = reaction.f; - if ((flags2 & DERIVED) !== 0) { - mark_effects( - /** @type {Derived} */ - reaction, - sources, - marked, - checked - ); - } else if ((flags2 & (ASYNC | BLOCK_EFFECT)) !== 0 && (flags2 & DIRTY) === 0 && depends_on(reaction, sources, checked)) { - set_signal_status(reaction, DIRTY); - schedule_effect( - /** @type {Effect} */ - reaction - ); - } - } - } -} -function depends_on(reaction, sources, checked) { - const depends = checked.get(reaction); - if (depends !== void 0) return depends; - if (reaction.deps !== null) { - for (const dep of reaction.deps) { - if (includes.call(sources, dep)) { - return true; - } - if ((dep.f & DERIVED) !== 0 && depends_on( - /** @type {Derived} */ - dep, - sources, - checked - )) { - checked.set( - /** @type {Derived} */ - dep, - true - ); - return true; - } - } - } - checked.set(reaction, false); - return false; -} -function schedule_effect(effect) { - current_batch.schedule(effect); -} -function reset_branch(effect, tracked) { - if ((effect.f & BRANCH_EFFECT) !== 0 && (effect.f & CLEAN) !== 0) { - return; - } - if ((effect.f & DIRTY) !== 0) { - tracked.d.push(effect); - } else if ((effect.f & MAYBE_DIRTY) !== 0) { - tracked.m.push(effect); - } - set_signal_status(effect, CLEAN); - var e = effect.first; - while (e !== null) { - reset_branch(e, tracked); - e = e.next; - } -} -function reset_all(effect) { - set_signal_status(effect, CLEAN); - var e = effect.first; - while (e !== null) { - reset_all(e); - e = e.next; - } -} -function createSubscriber(start) { - let subscribers = 0; - let version = source(0); - let stop; - return () => { - if (effect_tracking()) { - get(version); - render_effect(() => { - if (subscribers === 0) { - stop = untrack(() => start(() => increment(version))); - } - subscribers += 1; - return () => { - queue_micro_task(() => { - subscribers -= 1; - if (subscribers === 0) { - stop?.(); - stop = void 0; - increment(version); - } - }); - }; - }); - } - }; -} -var flags = EFFECT_TRANSPARENT | EFFECT_PRESERVED; -function boundary(node, props, children, transform_error) { - new Boundary(node, props, children, transform_error); -} -class Boundary { - /** @type {Boundary | null} */ - parent; - is_pending = false; - /** - * API-level transformError transform function. Transforms errors before they reach the `failed` snippet. - * Inherited from parent boundary, or defaults to identity. - * @type {(error: unknown) => unknown} - */ - transform_error; - /** @type {TemplateNode} */ - #anchor; - /** @type {TemplateNode | null} */ - #hydrate_open = hydrating ? hydrate_node : null; - /** @type {BoundaryProps} */ - #props; - /** @type {((anchor: Node) => void)} */ - #children; - /** @type {Effect} */ - #effect; - /** @type {Effect | null} */ - #main_effect = null; - /** @type {Effect | null} */ - #pending_effect = null; - /** @type {Effect | null} */ - #failed_effect = null; - /** @type {DocumentFragment | null} */ - #offscreen_fragment = null; - #local_pending_count = 0; - #pending_count = 0; - #pending_count_update_queued = false; - /** @type {Set} */ - #dirty_effects = /* @__PURE__ */ new Set(); - /** @type {Set} */ - #maybe_dirty_effects = /* @__PURE__ */ new Set(); - /** - * A source containing the number of pending async deriveds/expressions. - * Only created if `$effect.pending()` is used inside the boundary, - * otherwise updating the source results in needless `Batch.ensure()` - * calls followed by no-op flushes - * @type {Source | null} - */ - #effect_pending = null; - #effect_pending_subscriber = createSubscriber(() => { - this.#effect_pending = source(this.#local_pending_count); - return () => { - this.#effect_pending = null; - }; - }); - /** - * @param {TemplateNode} node - * @param {BoundaryProps} props - * @param {((anchor: Node) => void)} children - * @param {((error: unknown) => unknown) | undefined} [transform_error] - */ - constructor(node, props, children, transform_error) { - this.#anchor = node; - this.#props = props; - this.#children = (anchor) => { - var effect = ( - /** @type {Effect} */ - active_effect - ); - effect.b = this; - effect.f |= BOUNDARY_EFFECT; - children(anchor); - }; - this.parent = /** @type {Effect} */ - active_effect.b; - this.transform_error = transform_error ?? this.parent?.transform_error ?? ((e) => e); - this.#effect = block(() => { - if (hydrating) { - const comment = ( - /** @type {Comment} */ - this.#hydrate_open - ); - hydrate_next(); - const server_rendered_pending = comment.data === HYDRATION_START_ELSE; - const server_rendered_failed = comment.data.startsWith(HYDRATION_START_FAILED); - if (server_rendered_failed) { - const serialized_error = JSON.parse(comment.data.slice(HYDRATION_START_FAILED.length)); - this.#hydrate_failed_content(serialized_error); - } else if (server_rendered_pending) { - this.#hydrate_pending_content(); - } else { - this.#hydrate_resolved_content(); - } - } else { - this.#render(); - } - }, flags); - if (hydrating) { - this.#anchor = hydrate_node; - } - } - #hydrate_resolved_content() { - try { - this.#main_effect = branch(() => this.#children(this.#anchor)); - } catch (error) { - this.error(error); - } - } - /** - * @param {unknown} error The deserialized error from the server's hydration comment - */ - #hydrate_failed_content(error) { - const failed = this.#props.failed; - if (!failed) return; - this.#failed_effect = branch(() => { - failed( - this.#anchor, - () => error, - () => () => { - } - ); - }); - } - #hydrate_pending_content() { - const pending = this.#props.pending; - if (!pending) return; - this.is_pending = true; - this.#pending_effect = branch(() => pending(this.#anchor)); - queue_micro_task(() => { - var fragment = this.#offscreen_fragment = document.createDocumentFragment(); - var anchor = create_text(); - fragment.append(anchor); - this.#main_effect = this.#run(() => { - return branch(() => this.#children(anchor)); - }); - if (this.#pending_count === 0) { - this.#anchor.before(fragment); - this.#offscreen_fragment = null; - pause_effect( - /** @type {Effect} */ - this.#pending_effect, - () => { - this.#pending_effect = null; - } - ); - this.#resolve( - /** @type {Batch} */ - current_batch - ); - } - }); - } - #render() { - try { - this.is_pending = this.has_pending_snippet(); - this.#pending_count = 0; - this.#local_pending_count = 0; - this.#main_effect = branch(() => { - this.#children(this.#anchor); - }); - if (this.#pending_count > 0) { - var fragment = this.#offscreen_fragment = document.createDocumentFragment(); - move_effect(this.#main_effect, fragment); - const pending = ( - /** @type {(anchor: Node) => void} */ - this.#props.pending - ); - this.#pending_effect = branch(() => pending(this.#anchor)); - } else { - this.#resolve( - /** @type {Batch} */ - current_batch - ); - } - } catch (error) { - this.error(error); - } - } - /** - * @param {Batch} batch - */ - #resolve(batch) { - this.is_pending = false; - batch.transfer_effects(this.#dirty_effects, this.#maybe_dirty_effects); - } - /** - * Defer an effect inside a pending boundary until the boundary resolves - * @param {Effect} effect - */ - defer_effect(effect) { - defer_effect(effect, this.#dirty_effects, this.#maybe_dirty_effects); - } - /** - * Returns `false` if the effect exists inside a boundary whose pending snippet is shown - * @returns {boolean} - */ - is_rendered() { - return !this.is_pending && (!this.parent || this.parent.is_rendered()); - } - has_pending_snippet() { - return !!this.#props.pending; - } - /** - * @template T - * @param {() => T} fn - */ - #run(fn) { - var previous_effect = active_effect; - var previous_reaction = active_reaction; - var previous_ctx = component_context; - set_active_effect(this.#effect); - set_active_reaction(this.#effect); - set_component_context(this.#effect.ctx); - try { - Batch.ensure(); - return fn(); - } catch (e) { - handle_error(e); - return null; - } finally { - set_active_effect(previous_effect); - set_active_reaction(previous_reaction); - set_component_context(previous_ctx); - } - } - /** - * Updates the pending count associated with the currently visible pending snippet, - * if any, such that we can replace the snippet with content once work is done - * @param {1 | -1} d - * @param {Batch} batch - */ - #update_pending_count(d, batch) { - if (!this.has_pending_snippet()) { - if (this.parent) { - this.parent.#update_pending_count(d, batch); - } - return; - } - this.#pending_count += d; - if (this.#pending_count === 0) { - this.#resolve(batch); - if (this.#pending_effect) { - pause_effect(this.#pending_effect, () => { - this.#pending_effect = null; - }); - } - if (this.#offscreen_fragment) { - this.#anchor.before(this.#offscreen_fragment); - this.#offscreen_fragment = null; - } - } - } - /** - * Update the source that powers `$effect.pending()` inside this boundary, - * and controls when the current `pending` snippet (if any) is removed. - * Do not call from inside the class - * @param {1 | -1} d - * @param {Batch} batch - */ - update_pending_count(d, batch) { - this.#update_pending_count(d, batch); - this.#local_pending_count += d; - if (!this.#effect_pending || this.#pending_count_update_queued) return; - this.#pending_count_update_queued = true; - queue_micro_task(() => { - this.#pending_count_update_queued = false; - if (this.#effect_pending) { - internal_set(this.#effect_pending, this.#local_pending_count); - } - }); - } - get_effect_pending() { - this.#effect_pending_subscriber(); - return get( - /** @type {Source} */ - this.#effect_pending - ); - } - /** @param {unknown} error */ - error(error) { - var onerror = this.#props.onerror; - let failed = this.#props.failed; - if (!onerror && !failed) { - throw error; - } - if (this.#main_effect) { - destroy_effect(this.#main_effect); - this.#main_effect = null; - } - if (this.#pending_effect) { - destroy_effect(this.#pending_effect); - this.#pending_effect = null; - } - if (this.#failed_effect) { - destroy_effect(this.#failed_effect); - this.#failed_effect = null; - } - if (hydrating) { - set_hydrate_node( - /** @type {TemplateNode} */ - this.#hydrate_open - ); - next(); - set_hydrate_node(skip_nodes()); - } - var did_reset = false; - var calling_on_error = false; - const reset = () => { - if (did_reset) { - svelte_boundary_reset_noop(); - return; - } - did_reset = true; - if (calling_on_error) { - svelte_boundary_reset_onerror(); - } - if (this.#failed_effect !== null) { - pause_effect(this.#failed_effect, () => { - this.#failed_effect = null; - }); - } - this.#run(() => { - this.#render(); - }); - }; - const handle_error_result = (transformed_error) => { - try { - calling_on_error = true; - onerror?.(transformed_error, reset); - calling_on_error = false; - } catch (error2) { - invoke_error_boundary(error2, this.#effect && this.#effect.parent); - } - if (failed) { - this.#failed_effect = this.#run(() => { - try { - return branch(() => { - var effect = ( - /** @type {Effect} */ - active_effect - ); - effect.b = this; - effect.f |= BOUNDARY_EFFECT; - failed( - this.#anchor, - () => transformed_error, - () => reset - ); - }); - } catch (error2) { - invoke_error_boundary( - error2, - /** @type {Effect} */ - this.#effect.parent - ); - return null; - } - }); - } - }; - queue_micro_task(() => { - var result; - try { - result = this.transform_error(error); - } catch (e) { - invoke_error_boundary(e, this.#effect && this.#effect.parent); - return; - } - if (result !== null && typeof result === "object" && typeof /** @type {any} */ - result.then === "function") { - result.then( - handle_error_result, - /** @param {unknown} e */ - (e) => invoke_error_boundary(e, this.#effect && this.#effect.parent) - ); - } else { - handle_error_result(result); - } - }); - } -} -function destroy_derived_effects(derived2) { - var effects = derived2.effects; - if (effects !== null) { - derived2.effects = null; - for (var i = 0; i < effects.length; i += 1) { - destroy_effect( - /** @type {Effect} */ - effects[i] - ); - } - } -} -function get_derived_parent_effect(derived2) { - var parent = derived2.parent; - while (parent !== null) { - if ((parent.f & DERIVED) === 0) { - return (parent.f & DESTROYED) === 0 ? ( - /** @type {Effect} */ - parent - ) : null; - } - parent = parent.parent; - } - return null; -} -function execute_derived(derived2) { - var value; - var prev_active_effect = active_effect; - set_active_effect(get_derived_parent_effect(derived2)); - { - try { - derived2.f &= ~WAS_MARKED; - destroy_derived_effects(derived2); - value = update_reaction(derived2); - } finally { - set_active_effect(prev_active_effect); - } - } - return value; -} -function update_derived(derived2) { - var value = execute_derived(derived2); - if (!derived2.equals(value)) { - derived2.wv = increment_write_version(); - if (!current_batch?.is_fork || derived2.deps === null) { - if (current_batch !== null) { - current_batch.capture(derived2, value, true); - } else { - derived2.v = value; - } - if (derived2.deps === null) { - set_signal_status(derived2, CLEAN); - return; - } - } - } - if (is_destroying_effect) { - return; - } - if (batch_values !== null) { - if (effect_tracking() || current_batch?.is_fork) { - batch_values.set(derived2, value); - } - } else { - update_derived_status(derived2); - } -} -function freeze_derived_effects(derived2) { - if (derived2.effects === null) return; - for (const e of derived2.effects) { - if (e.teardown || e.ac) { - e.teardown?.(); - e.ac?.abort(STALE_REACTION); - e.teardown = noop; - e.ac = null; - remove_reactions(e, 0); - destroy_effect_children(e); - } - } -} -function unfreeze_derived_effects(derived2) { - if (derived2.effects === null) return; - for (const e of derived2.effects) { - if (e.teardown) { - update_effect(e); - } - } -} -let eager_effects = /* @__PURE__ */ new Set(); -const old_values = /* @__PURE__ */ new Map(); -let eager_effects_deferred = false; -function source(v, stack) { - var signal = { - f: 0, - // TODO ideally we could skip this altogether, but it causes type errors - v, - reactions: null, - equals, - rv: 0, - wv: 0 - }; - return signal; -} -// @__NO_SIDE_EFFECTS__ -function state(v, stack) { - const s = source(v); - push_reaction_value(s); - return s; -} -// @__NO_SIDE_EFFECTS__ -function mutable_source(initial_value, immutable = false, trackable = true) { - const s = source(initial_value); - if (!immutable) { - s.equals = safe_equals; - } - return s; -} -function set(source2, value, should_proxy = false) { - if (active_reaction !== null && // since we are untracking the function inside `$inspect.with` we need to add this check - // to ensure we error if state is set inside an inspect effect - (!untracking || (active_reaction.f & EAGER_EFFECT) !== 0) && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT | ASYNC | EAGER_EFFECT)) !== 0 && (current_sources === null || !includes.call(current_sources, source2))) { - state_unsafe_mutation(); - } - let new_value = should_proxy ? proxy(value) : value; - return internal_set(source2, new_value, legacy_updates); -} -function internal_set(source2, value, updated_during_traversal = null) { - if (!source2.equals(value)) { - old_values.set(source2, is_destroying_effect ? value : source2.v); - var batch = Batch.ensure(); - batch.capture(source2, value); - if ((source2.f & DERIVED) !== 0) { - const derived2 = ( - /** @type {Derived} */ - source2 - ); - if ((source2.f & DIRTY) !== 0) { - execute_derived(derived2); - } - if (batch_values === null) { - update_derived_status(derived2); - } - } - source2.wv = increment_write_version(); - mark_reactions(source2, DIRTY, updated_during_traversal); - if (active_effect !== null && (active_effect.f & CLEAN) !== 0 && (active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0) { - if (untracked_writes === null) { - set_untracked_writes([source2]); - } else { - untracked_writes.push(source2); - } - } - if (!batch.is_fork && eager_effects.size > 0 && !eager_effects_deferred) { - flush_eager_effects(); - } - } - return value; -} -function flush_eager_effects() { - eager_effects_deferred = false; - for (const effect of eager_effects) { - if ((effect.f & CLEAN) !== 0) { - set_signal_status(effect, MAYBE_DIRTY); - } - if (is_dirty(effect)) { - update_effect(effect); - } - } - eager_effects.clear(); -} -function increment(source2) { - set(source2, source2.v + 1); -} -function mark_reactions(signal, status, updated_during_traversal) { - var reactions = signal.reactions; - if (reactions === null) return; - var length = reactions.length; - for (var i = 0; i < length; i++) { - var reaction = reactions[i]; - var flags2 = reaction.f; - var not_dirty = (flags2 & DIRTY) === 0; - if (not_dirty) { - set_signal_status(reaction, status); - } - if ((flags2 & DERIVED) !== 0) { - var derived2 = ( - /** @type {Derived} */ - reaction - ); - batch_values?.delete(derived2); - if ((flags2 & WAS_MARKED) === 0) { - if (flags2 & CONNECTED) { - reaction.f |= WAS_MARKED; - } - mark_reactions(derived2, MAYBE_DIRTY, updated_during_traversal); - } - } else if (not_dirty) { - var effect = ( - /** @type {Effect} */ - reaction - ); - if ((flags2 & BLOCK_EFFECT) !== 0 && eager_block_effects !== null) { - eager_block_effects.add(effect); - } - if (updated_during_traversal !== null) { - updated_during_traversal.push(effect); - } else { - schedule_effect(effect); - } - } - } -} -function proxy(value) { - if (typeof value !== "object" || value === null || STATE_SYMBOL in value) { - return value; - } - const prototype = get_prototype_of(value); - if (prototype !== object_prototype && prototype !== array_prototype) { - return value; - } - var sources = /* @__PURE__ */ new Map(); - var is_proxied_array = is_array(value); - var version = /* @__PURE__ */ state(0); - var parent_version = update_version; - var with_parent = (fn) => { - if (update_version === parent_version) { - return fn(); - } - var reaction = active_reaction; - var version2 = update_version; - set_active_reaction(null); - set_update_version(parent_version); - var result = fn(); - set_active_reaction(reaction); - set_update_version(version2); - return result; - }; - if (is_proxied_array) { - sources.set("length", /* @__PURE__ */ state( - /** @type {any[]} */ - value.length - )); - } - return new Proxy( - /** @type {any} */ - value, - { - defineProperty(_, prop, descriptor) { - if (!("value" in descriptor) || descriptor.configurable === false || descriptor.enumerable === false || descriptor.writable === false) { - state_descriptors_fixed(); - } - var s = sources.get(prop); - if (s === void 0) { - with_parent(() => { - var s2 = /* @__PURE__ */ state(descriptor.value); - sources.set(prop, s2); - return s2; - }); - } else { - set(s, descriptor.value, true); - } - return true; - }, - deleteProperty(target, prop) { - var s = sources.get(prop); - if (s === void 0) { - if (prop in target) { - const s2 = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); - sources.set(prop, s2); - increment(version); - } - } else { - set(s, UNINITIALIZED); - increment(version); - } - return true; - }, - get(target, prop, receiver) { - if (prop === STATE_SYMBOL) { - return value; - } - var s = sources.get(prop); - var exists = prop in target; - if (s === void 0 && (!exists || get_descriptor(target, prop)?.writable)) { - s = with_parent(() => { - var p = proxy(exists ? target[prop] : UNINITIALIZED); - var s2 = /* @__PURE__ */ state(p); - return s2; - }); - sources.set(prop, s); - } - if (s !== void 0) { - var v = get(s); - return v === UNINITIALIZED ? void 0 : v; - } - return Reflect.get(target, prop, receiver); - }, - getOwnPropertyDescriptor(target, prop) { - var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); - if (descriptor && "value" in descriptor) { - var s = sources.get(prop); - if (s) descriptor.value = get(s); - } else if (descriptor === void 0) { - var source2 = sources.get(prop); - var value2 = source2?.v; - if (source2 !== void 0 && value2 !== UNINITIALIZED) { - return { - enumerable: true, - configurable: true, - value: value2, - writable: true - }; - } - } - return descriptor; - }, - has(target, prop) { - if (prop === STATE_SYMBOL) { - return true; - } - var s = sources.get(prop); - var has = s !== void 0 && s.v !== UNINITIALIZED || Reflect.has(target, prop); - if (s !== void 0 || active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) { - if (s === void 0) { - s = with_parent(() => { - var p = has ? proxy(target[prop]) : UNINITIALIZED; - var s2 = /* @__PURE__ */ state(p); - return s2; - }); - sources.set(prop, s); - } - var value2 = get(s); - if (value2 === UNINITIALIZED) { - return false; - } - } - return has; - }, - set(target, prop, value2, receiver) { - var s = sources.get(prop); - var has = prop in target; - if (is_proxied_array && prop === "length") { - for (var i = value2; i < /** @type {Source} */ - s.v; i += 1) { - var other_s = sources.get(i + ""); - if (other_s !== void 0) { - set(other_s, UNINITIALIZED); - } else if (i in target) { - other_s = with_parent(() => /* @__PURE__ */ state(UNINITIALIZED)); - sources.set(i + "", other_s); - } - } - } - if (s === void 0) { - if (!has || get_descriptor(target, prop)?.writable) { - s = with_parent(() => /* @__PURE__ */ state(void 0)); - set(s, proxy(value2)); - sources.set(prop, s); - } - } else { - has = s.v !== UNINITIALIZED; - var p = with_parent(() => proxy(value2)); - set(s, p); - } - var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); - if (descriptor?.set) { - descriptor.set.call(receiver, value2); - } - if (!has) { - if (is_proxied_array && typeof prop === "string") { - var ls = ( - /** @type {Source} */ - sources.get("length") - ); - var n = Number(prop); - if (Number.isInteger(n) && n >= ls.v) { - set(ls, n + 1); - } - } - increment(version); - } - return true; - }, - ownKeys(target) { - get(version); - var own_keys = Reflect.ownKeys(target).filter((key2) => { - var source3 = sources.get(key2); - return source3 === void 0 || source3.v !== UNINITIALIZED; - }); - for (var [key, source2] of sources) { - if (source2.v !== UNINITIALIZED && !(key in target)) { - own_keys.push(key); - } - } - return own_keys; - }, - setPrototypeOf() { - state_prototype_fixed(); - } - } - ); -} -var $window; -var first_child_getter; -var next_sibling_getter; -function init_operations() { - if ($window !== void 0) { - return; - } - $window = window; - var element_prototype = Element.prototype; - var node_prototype = Node.prototype; - var text_prototype = Text.prototype; - first_child_getter = get_descriptor(node_prototype, "firstChild").get; - next_sibling_getter = get_descriptor(node_prototype, "nextSibling").get; - if (is_extensible(element_prototype)) { - element_prototype.__click = void 0; - element_prototype.__className = void 0; - element_prototype.__attributes = null; - element_prototype.__style = void 0; - element_prototype.__e = void 0; - } - if (is_extensible(text_prototype)) { - text_prototype.__t = void 0; - } -} -function create_text(value = "") { - return document.createTextNode(value); -} -// @__NO_SIDE_EFFECTS__ -function get_first_child(node) { - return ( - /** @type {TemplateNode | null} */ - first_child_getter.call(node) - ); -} -// @__NO_SIDE_EFFECTS__ -function get_next_sibling(node) { - return ( - /** @type {TemplateNode | null} */ - next_sibling_getter.call(node) - ); -} -function clear_text_content(node) { - node.textContent = ""; -} -function without_reactive_context(fn) { - var previous_reaction = active_reaction; - var previous_effect = active_effect; - set_active_reaction(null); - set_active_effect(null); - try { - return fn(); - } finally { - set_active_reaction(previous_reaction); - set_active_effect(previous_effect); - } -} -function push_effect(effect, parent_effect) { - var parent_last = parent_effect.last; - if (parent_last === null) { - parent_effect.last = parent_effect.first = effect; - } else { - parent_last.next = effect; - effect.prev = parent_last; - parent_effect.last = effect; - } -} -function create_effect(type, fn) { - var parent = active_effect; - if (parent !== null && (parent.f & INERT) !== 0) { - type |= INERT; - } - var effect = { - ctx: component_context, - deps: null, - nodes: null, - f: type | DIRTY | CONNECTED, - first: null, - fn, - last: null, - next: null, - parent, - b: parent && parent.b, - prev: null, - teardown: null, - wv: 0, - ac: null - }; - current_batch?.register_created_effect(effect); - var e = effect; - if ((type & EFFECT) !== 0) { - if (collected_effects !== null) { - collected_effects.push(effect); - } else { - Batch.ensure().schedule(effect); - } - } else if (fn !== null) { - try { - update_effect(effect); - } catch (e2) { - destroy_effect(effect); - throw e2; - } - if (e.deps === null && e.teardown === null && e.nodes === null && e.first === e.last && // either `null`, or a singular child - (e.f & EFFECT_PRESERVED) === 0) { - e = e.first; - if ((type & BLOCK_EFFECT) !== 0 && (type & EFFECT_TRANSPARENT) !== 0 && e !== null) { - e.f |= EFFECT_TRANSPARENT; - } - } - } - if (e !== null) { - e.parent = parent; - if (parent !== null) { - push_effect(e, parent); - } - if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0 && (type & ROOT_EFFECT) === 0) { - var derived2 = ( - /** @type {Derived} */ - active_reaction - ); - (derived2.effects ??= []).push(e); - } - } - return effect; -} -function effect_tracking() { - return active_reaction !== null && !untracking; -} -function create_user_effect(fn) { - return create_effect(EFFECT | USER_EFFECT, fn); -} -function component_root(fn) { - Batch.ensure(); - const effect = create_effect(ROOT_EFFECT | EFFECT_PRESERVED, fn); - return (options = {}) => { - return new Promise((fulfil) => { - if (options.outro) { - pause_effect(effect, () => { - destroy_effect(effect); - fulfil(void 0); - }); - } else { - destroy_effect(effect); - fulfil(void 0); - } - }); - }; -} -function render_effect(fn, flags2 = 0) { - return create_effect(RENDER_EFFECT | flags2, fn); -} -function block(fn, flags2 = 0) { - var effect = create_effect(BLOCK_EFFECT | flags2, fn); - return effect; -} -function branch(fn) { - return create_effect(BRANCH_EFFECT | EFFECT_PRESERVED, fn); -} -function execute_effect_teardown(effect) { - var teardown = effect.teardown; - if (teardown !== null) { - const previously_destroying_effect = is_destroying_effect; - const previous_reaction = active_reaction; - set_is_destroying_effect(true); - set_active_reaction(null); - try { - teardown.call(null); - } finally { - set_is_destroying_effect(previously_destroying_effect); - set_active_reaction(previous_reaction); - } - } -} -function destroy_effect_children(signal, remove_dom = false) { - var effect = signal.first; - signal.first = signal.last = null; - while (effect !== null) { - const controller = effect.ac; - if (controller !== null) { - without_reactive_context(() => { - controller.abort(STALE_REACTION); - }); - } - var next2 = effect.next; - if ((effect.f & ROOT_EFFECT) !== 0) { - effect.parent = null; - } else { - destroy_effect(effect, remove_dom); - } - effect = next2; - } -} -function destroy_block_effect_children(signal) { - var effect = signal.first; - while (effect !== null) { - var next2 = effect.next; - if ((effect.f & BRANCH_EFFECT) === 0) { - destroy_effect(effect); - } - effect = next2; - } -} -function destroy_effect(effect, remove_dom = true) { - var removed = false; - if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes !== null && effect.nodes.end !== null) { - remove_effect_dom( - effect.nodes.start, - /** @type {TemplateNode} */ - effect.nodes.end - ); - removed = true; - } - set_signal_status(effect, DESTROYING); - destroy_effect_children(effect, remove_dom && !removed); - remove_reactions(effect, 0); - var transitions = effect.nodes && effect.nodes.t; - if (transitions !== null) { - for (const transition of transitions) { - transition.stop(); - } - } - execute_effect_teardown(effect); - effect.f ^= DESTROYING; - effect.f |= DESTROYED; - var parent = effect.parent; - if (parent !== null && parent.first !== null) { - unlink_effect(effect); - } - effect.next = effect.prev = effect.teardown = effect.ctx = effect.deps = effect.fn = effect.nodes = effect.ac = effect.b = null; -} -function remove_effect_dom(node, end) { - while (node !== null) { - var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); - node.remove(); - node = next2; - } -} -function unlink_effect(effect) { - var parent = effect.parent; - var prev = effect.prev; - var next2 = effect.next; - if (prev !== null) prev.next = next2; - if (next2 !== null) next2.prev = prev; - if (parent !== null) { - if (parent.first === effect) parent.first = next2; - if (parent.last === effect) parent.last = prev; - } -} -function pause_effect(effect, callback, destroy = true) { - var transitions = []; - pause_children(effect, transitions, true); - var fn = () => { - if (destroy) destroy_effect(effect); - if (callback) callback(); - }; - var remaining = transitions.length; - if (remaining > 0) { - var check = () => --remaining || fn(); - for (var transition of transitions) { - transition.out(check); - } - } else { - fn(); - } -} -function pause_children(effect, transitions, local) { - if ((effect.f & INERT) !== 0) return; - effect.f ^= INERT; - var t = effect.nodes && effect.nodes.t; - if (t !== null) { - for (const transition of t) { - if (transition.is_global || local) { - transitions.push(transition); - } - } - } - var child = effect.first; - while (child !== null) { - var sibling = child.next; - var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || // If this is a branch effect without a block effect parent, - // it means the parent block effect was pruned. In that case, - // transparency information was transferred to the branch effect. - (child.f & BRANCH_EFFECT) !== 0 && (effect.f & BLOCK_EFFECT) !== 0; - pause_children(child, transitions, transparent ? local : false); - child = sibling; - } -} -function move_effect(effect, fragment) { - if (!effect.nodes) return; - var node = effect.nodes.start; - var end = effect.nodes.end; - while (node !== null) { - var next2 = node === end ? null : /* @__PURE__ */ get_next_sibling(node); - fragment.append(node); - node = next2; - } -} -let is_updating_effect = false; -let is_destroying_effect = false; -function set_is_destroying_effect(value) { - is_destroying_effect = value; -} -let active_reaction = null; -let untracking = false; -function set_active_reaction(reaction) { - active_reaction = reaction; -} -let active_effect = null; -function set_active_effect(effect) { - active_effect = effect; -} -let current_sources = null; -function push_reaction_value(value) { - if (active_reaction !== null && true) { - if (current_sources === null) { - current_sources = [value]; - } else { - current_sources.push(value); - } - } -} -let new_deps = null; -let skipped_deps = 0; -let untracked_writes = null; -function set_untracked_writes(value) { - untracked_writes = value; -} -let write_version = 1; -let read_version = 0; -let update_version = read_version; -function set_update_version(value) { - update_version = value; -} -function increment_write_version() { - return ++write_version; -} -function is_dirty(reaction) { - var flags2 = reaction.f; - if ((flags2 & DIRTY) !== 0) { - return true; - } - if (flags2 & DERIVED) { - reaction.f &= ~WAS_MARKED; - } - if ((flags2 & MAYBE_DIRTY) !== 0) { - var dependencies = ( - /** @type {Value[]} */ - reaction.deps - ); - var length = dependencies.length; - for (var i = 0; i < length; i++) { - var dependency = dependencies[i]; - if (is_dirty( - /** @type {Derived} */ - dependency - )) { - update_derived( - /** @type {Derived} */ - dependency - ); - } - if (dependency.wv > reaction.wv) { - return true; - } - } - if ((flags2 & CONNECTED) !== 0 && // During time traveling we don't want to reset the status so that - // traversal of the graph in the other batches still happens - batch_values === null) { - set_signal_status(reaction, CLEAN); - } - } - return false; -} -function schedule_possible_effect_self_invalidation(signal, effect, root2 = true) { - var reactions = signal.reactions; - if (reactions === null) return; - if (current_sources !== null && includes.call(current_sources, signal)) { - return; - } - for (var i = 0; i < reactions.length; i++) { - var reaction = reactions[i]; - if ((reaction.f & DERIVED) !== 0) { - schedule_possible_effect_self_invalidation( - /** @type {Derived} */ - reaction, - effect, - false - ); - } else if (effect === reaction) { - if (root2) { - set_signal_status(reaction, DIRTY); - } else if ((reaction.f & CLEAN) !== 0) { - set_signal_status(reaction, MAYBE_DIRTY); - } - schedule_effect( - /** @type {Effect} */ - reaction - ); - } - } -} -function update_reaction(reaction) { - var previous_deps = new_deps; - var previous_skipped_deps = skipped_deps; - var previous_untracked_writes = untracked_writes; - var previous_reaction = active_reaction; - var previous_sources = current_sources; - var previous_component_context = component_context; - var previous_untracking = untracking; - var previous_update_version = update_version; - var flags2 = reaction.f; - new_deps = /** @type {null | Value[]} */ - null; - skipped_deps = 0; - untracked_writes = null; - active_reaction = (flags2 & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - current_sources = null; - set_component_context(reaction.ctx); - untracking = false; - update_version = ++read_version; - if (reaction.ac !== null) { - without_reactive_context(() => { - reaction.ac.abort(STALE_REACTION); - }); - reaction.ac = null; - } - try { - reaction.f |= REACTION_IS_UPDATING; - var fn = ( - /** @type {Function} */ - reaction.fn - ); - var result = fn(); - reaction.f |= REACTION_RAN; - var deps = reaction.deps; - var is_fork = current_batch?.is_fork; - if (new_deps !== null) { - var i; - if (!is_fork) { - remove_reactions(reaction, skipped_deps); - } - if (deps !== null && skipped_deps > 0) { - deps.length = skipped_deps + new_deps.length; - for (i = 0; i < new_deps.length; i++) { - deps[skipped_deps + i] = new_deps[i]; - } - } else { - reaction.deps = deps = new_deps; - } - if (effect_tracking() && (reaction.f & CONNECTED) !== 0) { - for (i = skipped_deps; i < deps.length; i++) { - (deps[i].reactions ??= []).push(reaction); - } - } - } else if (!is_fork && deps !== null && skipped_deps < deps.length) { - remove_reactions(reaction, skipped_deps); - deps.length = skipped_deps; - } - if (is_runes() && untracked_writes !== null && !untracking && deps !== null && (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0) { - for (i = 0; i < /** @type {Source[]} */ - untracked_writes.length; i++) { - schedule_possible_effect_self_invalidation( - untracked_writes[i], - /** @type {Effect} */ - reaction - ); - } - } - if (previous_reaction !== null && previous_reaction !== reaction) { - read_version++; - if (previous_reaction.deps !== null) { - for (let i2 = 0; i2 < previous_skipped_deps; i2 += 1) { - previous_reaction.deps[i2].rv = read_version; - } - } - if (previous_deps !== null) { - for (const dep of previous_deps) { - dep.rv = read_version; - } - } - if (untracked_writes !== null) { - if (previous_untracked_writes === null) { - previous_untracked_writes = untracked_writes; - } else { - previous_untracked_writes.push(.../** @type {Source[]} */ - untracked_writes); - } - } - } - if ((reaction.f & ERROR_VALUE) !== 0) { - reaction.f ^= ERROR_VALUE; - } - return result; - } catch (error) { - return handle_error(error); - } finally { - reaction.f ^= REACTION_IS_UPDATING; - new_deps = previous_deps; - skipped_deps = previous_skipped_deps; - untracked_writes = previous_untracked_writes; - active_reaction = previous_reaction; - current_sources = previous_sources; - set_component_context(previous_component_context); - untracking = previous_untracking; - update_version = previous_update_version; - } -} -function remove_reaction(signal, dependency) { - let reactions = dependency.reactions; - if (reactions !== null) { - var index = index_of.call(reactions, signal); - if (index !== -1) { - var new_length = reactions.length - 1; - if (new_length === 0) { - reactions = dependency.reactions = null; - } else { - reactions[index] = reactions[new_length]; - reactions.pop(); - } - } - } - if (reactions === null && (dependency.f & DERIVED) !== 0 && // Destroying a child effect while updating a parent effect can cause a dependency to appear - // to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps` - // allows us to skip the expensive work of disconnecting and immediately reconnecting it - (new_deps === null || !includes.call(new_deps, dependency))) { - var derived2 = ( - /** @type {Derived} */ - dependency - ); - if ((derived2.f & CONNECTED) !== 0) { - derived2.f ^= CONNECTED; - derived2.f &= ~WAS_MARKED; - } - if (derived2.v !== UNINITIALIZED) { - update_derived_status(derived2); - } - freeze_derived_effects(derived2); - remove_reactions(derived2, 0); - } -} -function remove_reactions(signal, start_index) { - var dependencies = signal.deps; - if (dependencies === null) return; - for (var i = start_index; i < dependencies.length; i++) { - remove_reaction(signal, dependencies[i]); - } -} -function update_effect(effect) { - var flags2 = effect.f; - if ((flags2 & DESTROYED) !== 0) { - return; - } - set_signal_status(effect, CLEAN); - var previous_effect = active_effect; - var was_updating_effect = is_updating_effect; - active_effect = effect; - is_updating_effect = true; - try { - if ((flags2 & (BLOCK_EFFECT | MANAGED_EFFECT)) !== 0) { - destroy_block_effect_children(effect); - } else { - destroy_effect_children(effect); - } - execute_effect_teardown(effect); - var teardown = update_reaction(effect); - effect.teardown = typeof teardown === "function" ? teardown : null; - effect.wv = write_version; - var dep; - if (BROWSER && tracing_mode_flag && (effect.f & DIRTY) !== 0 && effect.deps !== null) ; - } finally { - is_updating_effect = was_updating_effect; - active_effect = previous_effect; - } -} -function get(signal) { - var flags2 = signal.f; - var is_derived = (flags2 & DERIVED) !== 0; - if (active_reaction !== null && !untracking) { - var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0; - if (!destroyed && (current_sources === null || !includes.call(current_sources, signal))) { - var deps = active_reaction.deps; - if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) { - if (signal.rv < read_version) { - signal.rv = read_version; - if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { - skipped_deps++; - } else if (new_deps === null) { - new_deps = [signal]; - } else { - new_deps.push(signal); - } - } - } else { - (active_reaction.deps ??= []).push(signal); - var reactions = signal.reactions; - if (reactions === null) { - signal.reactions = [active_reaction]; - } else if (!includes.call(reactions, active_reaction)) { - reactions.push(active_reaction); - } - } - } - } - if (is_destroying_effect && old_values.has(signal)) { - return old_values.get(signal); - } - if (is_derived) { - var derived2 = ( - /** @type {Derived} */ - signal - ); - if (is_destroying_effect) { - var value = derived2.v; - if ((derived2.f & CLEAN) === 0 && derived2.reactions !== null || depends_on_old_values(derived2)) { - value = execute_derived(derived2); - } - old_values.set(derived2, value); - return value; - } - var should_connect = (derived2.f & CONNECTED) === 0 && !untracking && active_reaction !== null && (is_updating_effect || (active_reaction.f & CONNECTED) !== 0); - var is_new = (derived2.f & REACTION_RAN) === 0; - if (is_dirty(derived2)) { - if (should_connect) { - derived2.f |= CONNECTED; - } - update_derived(derived2); - } - if (should_connect && !is_new) { - unfreeze_derived_effects(derived2); - reconnect(derived2); - } - } - if (batch_values?.has(signal)) { - return batch_values.get(signal); - } - if ((signal.f & ERROR_VALUE) !== 0) { - throw signal.v; - } - return signal.v; -} -function reconnect(derived2) { - derived2.f |= CONNECTED; - if (derived2.deps === null) return; - for (const dep of derived2.deps) { - (dep.reactions ??= []).push(derived2); - if ((dep.f & DERIVED) !== 0 && (dep.f & CONNECTED) === 0) { - unfreeze_derived_effects( - /** @type {Derived} */ - dep - ); - reconnect( - /** @type {Derived} */ - dep - ); - } - } -} -function depends_on_old_values(derived2) { - if (derived2.v === UNINITIALIZED) return true; - if (derived2.deps === null) return false; - for (const dep of derived2.deps) { - if (old_values.has(dep)) { - return true; - } - if ((dep.f & DERIVED) !== 0 && depends_on_old_values( - /** @type {Derived} */ - dep - )) { - return true; - } - } - return false; -} -function untrack(fn) { - var previous_untracking = untracking; - try { - untracking = true; - return fn(); - } finally { - untracking = previous_untracking; - } -} -const event_symbol = /* @__PURE__ */ Symbol("events"); -const all_registered_events = /* @__PURE__ */ new Set(); -const root_event_handles = /* @__PURE__ */ new Set(); -let last_propagated_event = null; -function handle_event_propagation(event) { - var handler_element = this; - var owner_document = ( - /** @type {Node} */ - handler_element.ownerDocument - ); - var event_name = event.type; - var path = event.composedPath?.() || []; - var current_target = ( - /** @type {null | Element} */ - path[0] || event.target - ); - last_propagated_event = event; - var path_idx = 0; - var handled_at = last_propagated_event === event && event[event_symbol]; - if (handled_at) { - var at_idx = path.indexOf(handled_at); - if (at_idx !== -1 && (handler_element === document || handler_element === /** @type {any} */ - window)) { - event[event_symbol] = handler_element; - return; - } - var handler_idx = path.indexOf(handler_element); - if (handler_idx === -1) { - return; - } - if (at_idx <= handler_idx) { - path_idx = at_idx; - } - } - current_target = /** @type {Element} */ - path[path_idx] || event.target; - if (current_target === handler_element) return; - define_property(event, "currentTarget", { - configurable: true, - get() { - return current_target || owner_document; - } - }); - var previous_reaction = active_reaction; - var previous_effect = active_effect; - set_active_reaction(null); - set_active_effect(null); - try { - var throw_error; - var other_errors = []; - while (current_target !== null) { - var parent_element = current_target.assignedSlot || current_target.parentNode || /** @type {any} */ - current_target.host || null; - try { - var delegated = current_target[event_symbol]?.[event_name]; - if (delegated != null && (!/** @type {any} */ - current_target.disabled || // DOM could've been updated already by the time this is reached, so we check this as well - // -> the target could not have been disabled because it emits the event in the first place - event.target === current_target)) { - delegated.call(current_target, event); - } - } catch (error) { - if (throw_error) { - other_errors.push(error); - } else { - throw_error = error; - } - } - if (event.cancelBubble || parent_element === handler_element || parent_element === null) { - break; - } - current_target = parent_element; - } - if (throw_error) { - for (let error of other_errors) { - queueMicrotask(() => { - throw error; - }); - } - throw throw_error; - } - } finally { - event[event_symbol] = handler_element; - delete event.currentTarget; - set_active_reaction(previous_reaction); - set_active_effect(previous_effect); - } -} -function assign_nodes(start, end) { - var effect = ( - /** @type {Effect} */ - active_effect - ); - if (effect.nodes === null) { - effect.nodes = { start, end, a: null, t: null }; - } -} -function mount(component, options) { - return _mount(component, options); -} -function hydrate(component, options) { - init_operations(); - options.intro = options.intro ?? false; - const target = options.target; - const was_hydrating = hydrating; - const previous_hydrate_node = hydrate_node; - try { - var anchor = /* @__PURE__ */ get_first_child(target); - while (anchor && (anchor.nodeType !== COMMENT_NODE || /** @type {Comment} */ - anchor.data !== HYDRATION_START)) { - anchor = /* @__PURE__ */ get_next_sibling(anchor); - } - if (!anchor) { - throw HYDRATION_ERROR; - } - set_hydrating(true); - set_hydrate_node( - /** @type {Comment} */ - anchor - ); - const instance = _mount(component, { ...options, anchor }); - set_hydrating(false); - return ( - /** @type {Exports} */ - instance - ); - } catch (error) { - if (error instanceof Error && error.message.split("\n").some((line) => line.startsWith("https://svelte.dev/e/"))) { - throw error; - } - if (error !== HYDRATION_ERROR) { - console.warn("Failed to hydrate: ", error); - } - if (options.recover === false) { - hydration_failed(); - } - init_operations(); - clear_text_content(target); - set_hydrating(false); - return mount(component, options); - } finally { - set_hydrating(was_hydrating); - set_hydrate_node(previous_hydrate_node); - } -} -const listeners = /* @__PURE__ */ new Map(); -function _mount(Component, { target, anchor, props = {}, events, context, intro = true, transformError }) { - init_operations(); - var component = void 0; - var unmount2 = component_root(() => { - var anchor_node = anchor ?? target.appendChild(create_text()); - boundary( - /** @type {TemplateNode} */ - anchor_node, - { - pending: () => { - } - }, - (anchor_node2) => { - push({}); - var ctx = ( - /** @type {ComponentContext} */ - component_context - ); - if (context) ctx.c = context; - if (events) { - props.$$events = events; - } - if (hydrating) { - assign_nodes( - /** @type {TemplateNode} */ - anchor_node2, - null - ); - } - component = Component(anchor_node2, props) || {}; - if (hydrating) { - active_effect.nodes.end = hydrate_node; - if (hydrate_node === null || hydrate_node.nodeType !== COMMENT_NODE || /** @type {Comment} */ - hydrate_node.data !== HYDRATION_END) { - hydration_mismatch(); - throw HYDRATION_ERROR; - } - } - pop(); - }, - transformError - ); - var registered_events = /* @__PURE__ */ new Set(); - var event_handle = (events2) => { - for (var i = 0; i < events2.length; i++) { - var event_name = events2[i]; - if (registered_events.has(event_name)) continue; - registered_events.add(event_name); - var passive = is_passive_event(event_name); - for (const node of [target, document]) { - var counts = listeners.get(node); - if (counts === void 0) { - counts = /* @__PURE__ */ new Map(); - listeners.set(node, counts); - } - var count = counts.get(event_name); - if (count === void 0) { - node.addEventListener(event_name, handle_event_propagation, { passive }); - counts.set(event_name, 1); - } else { - counts.set(event_name, count + 1); - } - } - } - }; - event_handle(array_from(all_registered_events)); - root_event_handles.add(event_handle); - return () => { - for (var event_name of registered_events) { - for (const node of [target, document]) { - var counts = ( - /** @type {Map} */ - listeners.get(node) - ); - var count = ( - /** @type {number} */ - counts.get(event_name) - ); - if (--count == 0) { - node.removeEventListener(event_name, handle_event_propagation); - counts.delete(event_name); - if (counts.size === 0) { - listeners.delete(node); - } - } else { - counts.set(event_name, count); - } - } - } - root_event_handles.delete(event_handle); - if (anchor_node !== anchor) { - anchor_node.parentNode?.removeChild(anchor_node); - } - }; - }); - mounted_components.set(component, unmount2); - return component; -} -let mounted_components = /* @__PURE__ */ new WeakMap(); -function unmount(component, options) { - const fn = mounted_components.get(component); - if (fn) { - mounted_components.delete(component); - return fn(options); - } - return Promise.resolve(); -} -function asClassComponent$1(component) { - return class extends Svelte4Component { - /** @param {any} options */ - constructor(options) { - super({ - component, - ...options - }); - } - }; -} -class Svelte4Component { - /** @type {any} */ - #events; - /** @type {Record} */ - #instance; - /** - * @param {ComponentConstructorOptions & { - * component: any; - * }} options - */ - constructor(options) { - var sources = /* @__PURE__ */ new Map(); - var add_source = (key, value) => { - var s = /* @__PURE__ */ mutable_source(value, false, false); - sources.set(key, s); - return s; - }; - const props = new Proxy( - { ...options.props || {}, $$events: {} }, - { - get(target, prop) { - return get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); - }, - has(target, prop) { - if (prop === LEGACY_PROPS) return true; - get(sources.get(prop) ?? add_source(prop, Reflect.get(target, prop))); - return Reflect.has(target, prop); - }, - set(target, prop, value) { - set(sources.get(prop) ?? add_source(prop, value), value); - return Reflect.set(target, prop, value); - } - } - ); - this.#instance = (options.hydrate ? hydrate : mount)(options.component, { - target: options.target, - anchor: options.anchor, - props, - context: options.context, - intro: options.intro ?? false, - recover: options.recover, - transformError: options.transformError - }); - if (!options?.props?.$$host || options.sync === false) { - flushSync(); - } - this.#events = props.$$events; - for (const key of Object.keys(this.#instance)) { - if (key === "$set" || key === "$destroy" || key === "$on") continue; - define_property(this, key, { - get() { - return this.#instance[key]; - }, - /** @param {any} value */ - set(value) { - this.#instance[key] = value; - }, - enumerable: true - }); - } - this.#instance.$set = /** @param {Record} next */ - (next2) => { - Object.assign(props, next2); - }; - this.#instance.$destroy = () => { - unmount(this.#instance); - }; - } - /** @param {Record} props */ - $set(props) { - this.#instance.$set(props); - } - /** - * @param {string} event - * @param {(...args: any[]) => any} callback - * @returns {any} - */ - $on(event, callback) { - this.#events[event] = this.#events[event] || []; - const cb = (...args) => callback.call(this, ...args); - this.#events[event].push(cb); - return () => { - this.#events[event] = this.#events[event].filter( - /** @param {any} fn */ - (fn) => fn !== cb - ); - }; - } - $destroy() { - this.#instance.$destroy(); - } -} -function asClassComponent(component) { - const component_constructor = asClassComponent$1(component); - const _render = (props, { context, csp, transformError } = {}) => { - const result = render(component, { props, context, csp, transformError }); - const munged = Object.defineProperties( - /** @type {LegacyRenderResult & PromiseLike} */ - {}, - { - css: { - value: { code: "", map: null } - }, - head: { - get: () => result.head - }, - html: { - get: () => result.body - }, - then: { - /** - * this is not type-safe, but honestly it's the best I can do right now, and it's a straightforward function. - * - * @template TResult1 - * @template [TResult2=never] - * @param { (value: LegacyRenderResult) => TResult1 } onfulfilled - * @param { (reason: unknown) => TResult2 } onrejected - */ - value: (onfulfilled, onrejected) => { - { - const user_result = onfulfilled({ - css: munged.css, - head: munged.head, - html: munged.html - }); - return Promise.resolve(user_result); - } - } - } - } - ); - return munged; - }; - component_constructor.render = _render; - return component_constructor; -} -function Root($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - let { - stores, - page, - constructors, - components = [], - form, - data_0 = null, - data_1 = null - } = $$props; - { - setContext("__svelte__", stores); - } - { - stores.page.set(page); - } - const Pyramid_1 = derived(() => constructors[1]); - if (constructors[1]) { - $$renderer2.push(""); - const Pyramid_0 = constructors[0]; - if (Pyramid_0) { - $$renderer2.push(""); - Pyramid_0($$renderer2, { - data: data_0, - form, - params: page.params, - children: ($$renderer3) => { - if (Pyramid_1()) { - $$renderer3.push(""); - Pyramid_1()($$renderer3, { data: data_1, form, params: page.params }); - $$renderer3.push(""); - } else { - $$renderer3.push(""); - $$renderer3.push(""); - } - }, - $$slots: { default: true } - }); - $$renderer2.push(""); - } else { - $$renderer2.push(""); - $$renderer2.push(""); - } - } else { - $$renderer2.push(""); - const Pyramid_0 = constructors[0]; - if (Pyramid_0) { - $$renderer2.push(""); - Pyramid_0($$renderer2, { data: data_0, form, params: page.params }); - $$renderer2.push(""); - } else { - $$renderer2.push(""); - $$renderer2.push(""); - } - } - $$renderer2.push(` `); - { - $$renderer2.push(""); - } - $$renderer2.push(``); - }); -} -const root = asClassComponent(Root); -export { - root as r, - safe_not_equal as s -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/shared-server.js - -```js -let public_env = {}; -function set_private_env(environment) { -} -function set_public_env(environment) { - public_env = environment; -} -export { - set_public_env as a, - public_env as p, - set_private_env as s -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/shared.js - -```js -import { json, text } from "@sveltejs/kit"; -import { SvelteKitError, HttpError } from "@sveltejs/kit/internal"; -import { with_request_store } from "@sveltejs/kit/internal/server"; -import { t as text_decoder, c as base64_decode, b as base64_encode } from "./utils.js"; -import { D as DevalueError, i as is_primitive, g as get_type, a as is_plain_object$1, e as enumerable_symbols, s as stringify_key, b as stringify_string, v as valid_array_indices, c as get_render_context, u as uneval, h as hydratable_serialization_failed } from "./render-context.js"; -import { e as experimental_async_required } from "./errors.js"; -const ENDPOINT_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"]; -const MUTATIVE_METHODS = ["POST", "PUT", "PATCH", "DELETE"]; -const PAGE_METHODS = ["GET", "POST", "HEAD"]; -function encode_native(array_buffer) { - return new Uint8Array(array_buffer).toBase64(); -} -function decode_native(base64) { - return Uint8Array.fromBase64(base64).buffer; -} -function encode_buffer(array_buffer) { - return Buffer.from(array_buffer).toString("base64"); -} -function decode_buffer(base64) { - return Uint8Array.from(Buffer.from(base64, "base64")).buffer; -} -function encode_legacy(array_buffer) { - const array = new Uint8Array(array_buffer); - let binary = ""; - const chunk_size = 32768; - for (let i = 0; i < array.length; i += chunk_size) { - const chunk = array.subarray(i, i + chunk_size); - binary += String.fromCharCode.apply(null, chunk); - } - return btoa(binary); -} -function decode_legacy(base64) { - const binary_string = atob(base64); - const len = binary_string.length; - const array = new Uint8Array(len); - for (let i = 0; i < len; i++) { - array[i] = binary_string.charCodeAt(i); - } - return array.buffer; -} -const native = typeof Uint8Array.fromBase64 === "function"; -const buffer = typeof process === "object" && process.versions?.node !== void 0; -const encode64 = native ? encode_native : buffer ? encode_buffer : encode_legacy; -const decode64 = native ? decode_native : buffer ? decode_buffer : decode_legacy; -const UNDEFINED = -1; -const HOLE = -2; -const NAN = -3; -const POSITIVE_INFINITY = -4; -const NEGATIVE_INFINITY = -5; -const NEGATIVE_ZERO = -6; -const SPARSE = -7; -function parse(serialized, revivers) { - return unflatten(JSON.parse(serialized), revivers); -} -function unflatten(parsed, revivers) { - if (typeof parsed === "number") return hydrate(parsed, true); - if (!Array.isArray(parsed) || parsed.length === 0) { - throw new Error("Invalid input"); - } - const values = ( - /** @type {any[]} */ - parsed - ); - const hydrated = Array(values.length); - let hydrating = null; - function hydrate(index, standalone = false) { - if (index === UNDEFINED) return void 0; - if (index === NAN) return NaN; - if (index === POSITIVE_INFINITY) return Infinity; - if (index === NEGATIVE_INFINITY) return -Infinity; - if (index === NEGATIVE_ZERO) return -0; - if (standalone || typeof index !== "number") { - throw new Error(`Invalid input`); - } - if (index in hydrated) return hydrated[index]; - const value = values[index]; - if (!value || typeof value !== "object") { - hydrated[index] = value; - } else if (Array.isArray(value)) { - if (typeof value[0] === "string") { - const type = value[0]; - const reviver = revivers && Object.hasOwn(revivers, type) ? revivers[type] : void 0; - if (reviver) { - let i = value[1]; - if (typeof i !== "number") { - i = values.push(value[1]) - 1; - } - hydrating ??= /* @__PURE__ */ new Set(); - if (hydrating.has(i)) { - throw new Error("Invalid circular reference"); - } - hydrating.add(i); - hydrated[index] = reviver(hydrate(i)); - hydrating.delete(i); - return hydrated[index]; - } - switch (type) { - case "Date": - hydrated[index] = new Date(value[1]); - break; - case "Set": - const set = /* @__PURE__ */ new Set(); - hydrated[index] = set; - for (let i = 1; i < value.length; i += 1) { - set.add(hydrate(value[i])); - } - break; - case "Map": - const map = /* @__PURE__ */ new Map(); - hydrated[index] = map; - for (let i = 1; i < value.length; i += 2) { - map.set(hydrate(value[i]), hydrate(value[i + 1])); - } - break; - case "RegExp": - hydrated[index] = new RegExp(value[1], value[2]); - break; - case "Object": { - const wrapped_index = value[1]; - if (typeof values[wrapped_index] === "object" && values[wrapped_index][0] !== "BigInt") { - throw new Error("Invalid input"); - } - hydrated[index] = Object(hydrate(wrapped_index)); - break; - } - case "BigInt": - hydrated[index] = BigInt(value[1]); - break; - case "null": - const obj = /* @__PURE__ */ Object.create(null); - hydrated[index] = obj; - for (let i = 1; i < value.length; i += 2) { - if (value[i] === "__proto__") { - throw new Error("Cannot parse an object with a `__proto__` property"); - } - obj[value[i]] = hydrate(value[i + 1]); - } - break; - case "Int8Array": - case "Uint8Array": - case "Uint8ClampedArray": - case "Int16Array": - case "Uint16Array": - case "Float16Array": - case "Int32Array": - case "Uint32Array": - case "Float32Array": - case "Float64Array": - case "BigInt64Array": - case "BigUint64Array": - case "DataView": { - if (values[value[1]][0] !== "ArrayBuffer") { - throw new Error("Invalid data"); - } - const TypedArrayConstructor = globalThis[type]; - const buffer2 = hydrate(value[1]); - hydrated[index] = value[2] !== void 0 ? new TypedArrayConstructor(buffer2, value[2], value[3]) : new TypedArrayConstructor(buffer2); - break; - } - case "ArrayBuffer": { - const base64 = value[1]; - if (typeof base64 !== "string") { - throw new Error("Invalid ArrayBuffer encoding"); - } - const arraybuffer = decode64(base64); - hydrated[index] = arraybuffer; - break; - } - case "Temporal.Duration": - case "Temporal.Instant": - case "Temporal.PlainDate": - case "Temporal.PlainTime": - case "Temporal.PlainDateTime": - case "Temporal.PlainMonthDay": - case "Temporal.PlainYearMonth": - case "Temporal.ZonedDateTime": { - const temporalName = type.slice(9); - hydrated[index] = Temporal[temporalName].from(value[1]); - break; - } - case "URL": { - const url = new URL(value[1]); - hydrated[index] = url; - break; - } - case "URLSearchParams": { - const url = new URLSearchParams(value[1]); - hydrated[index] = url; - break; - } - default: - throw new Error(`Unknown type ${type}`); - } - } else if (value[0] === SPARSE) { - const len = value[1]; - if (!Number.isInteger(len) || len < 0) { - throw new Error("Invalid input"); - } - const array = new Array(len); - hydrated[index] = array; - for (let i = 2; i < value.length; i += 2) { - const idx = value[i]; - if (!Number.isInteger(idx) || idx < 0 || idx >= len) { - throw new Error("Invalid input"); - } - array[idx] = hydrate(value[i + 1]); - } - } else { - const array = new Array(value.length); - hydrated[index] = array; - for (let i = 0; i < value.length; i += 1) { - const n = value[i]; - if (n === HOLE) continue; - array[i] = hydrate(n); - } - } - } else { - const object = {}; - hydrated[index] = object; - for (const key of Object.keys(value)) { - if (key === "__proto__") { - throw new Error("Cannot parse an object with a `__proto__` property"); - } - const n = value[key]; - object[key] = hydrate(n); - } - } - return hydrated[index]; - } - return hydrate(0); -} -function stringify$1(value, reducers) { - const stringified = []; - const indexes = /* @__PURE__ */ new Map(); - const custom = []; - if (reducers) { - for (const key of Object.getOwnPropertyNames(reducers)) { - custom.push({ key, fn: reducers[key] }); - } - } - const keys = []; - let p = 0; - function flatten(thing) { - if (thing === void 0) return UNDEFINED; - if (Number.isNaN(thing)) return NAN; - if (thing === Infinity) return POSITIVE_INFINITY; - if (thing === -Infinity) return NEGATIVE_INFINITY; - if (thing === 0 && 1 / thing < 0) return NEGATIVE_ZERO; - if (indexes.has(thing)) return ( - /** @type {number} */ - indexes.get(thing) - ); - const index2 = p++; - indexes.set(thing, index2); - for (const { key, fn } of custom) { - const value2 = fn(thing); - if (value2) { - stringified[index2] = `["${key}",${flatten(value2)}]`; - return index2; - } - } - if (typeof thing === "function") { - throw new DevalueError(`Cannot stringify a function`, keys, thing, value); - } else if (typeof thing === "symbol") { - throw new DevalueError(`Cannot stringify a Symbol primitive`, keys, thing, value); - } - let str = ""; - if (is_primitive(thing)) { - str = stringify_primitive(thing); - } else { - const type = get_type(thing); - switch (type) { - case "Number": - case "String": - case "Boolean": - case "BigInt": - str = `["Object",${flatten(thing.valueOf())}]`; - break; - case "Date": - const valid = !isNaN(thing.getDate()); - str = `["Date","${valid ? thing.toISOString() : ""}"]`; - break; - case "URL": - str = `["URL",${stringify_string(thing.toString())}]`; - break; - case "URLSearchParams": - str = `["URLSearchParams",${stringify_string(thing.toString())}]`; - break; - case "RegExp": - const { source, flags } = thing; - str = flags ? `["RegExp",${stringify_string(source)},"${flags}"]` : `["RegExp",${stringify_string(source)}]`; - break; - case "Array": { - let mostly_dense = false; - str = "["; - for (let i = 0; i < thing.length; i += 1) { - if (i > 0) str += ","; - if (Object.hasOwn(thing, i)) { - keys.push(`[${i}]`); - str += flatten(thing[i]); - keys.pop(); - } else if (mostly_dense) { - str += HOLE; - } else { - const populated_keys = valid_array_indices( - /** @type {any[]} */ - thing - ); - const population = populated_keys.length; - const d = String(thing.length).length; - const hole_cost = (thing.length - population) * 3; - const sparse_cost = 4 + d + population * (d + 1); - if (hole_cost > sparse_cost) { - str = "[" + SPARSE + "," + thing.length; - for (let j = 0; j < populated_keys.length; j++) { - const key = populated_keys[j]; - keys.push(`[${key}]`); - str += "," + key + "," + flatten(thing[key]); - keys.pop(); - } - break; - } else { - mostly_dense = true; - str += HOLE; - } - } - } - str += "]"; - break; - } - case "Set": - str = '["Set"'; - for (const value2 of thing) { - str += `,${flatten(value2)}`; - } - str += "]"; - break; - case "Map": - str = '["Map"'; - for (const [key, value2] of thing) { - keys.push(`.get(${is_primitive(key) ? stringify_primitive(key) : "..."})`); - str += `,${flatten(key)},${flatten(value2)}`; - keys.pop(); - } - str += "]"; - break; - case "Int8Array": - case "Uint8Array": - case "Uint8ClampedArray": - case "Int16Array": - case "Uint16Array": - case "Float16Array": - case "Int32Array": - case "Uint32Array": - case "Float32Array": - case "Float64Array": - case "BigInt64Array": - case "BigUint64Array": - case "DataView": { - const typedArray = thing; - str = '["' + type + '",' + flatten(typedArray.buffer); - if (typedArray.byteLength !== typedArray.buffer.byteLength) { - str += `,${typedArray.byteOffset},${typedArray.length}`; - } - str += "]"; - break; - } - case "ArrayBuffer": { - const arraybuffer = thing; - const base64 = encode64(arraybuffer); - str = `["ArrayBuffer","${base64}"]`; - break; - } - case "Temporal.Duration": - case "Temporal.Instant": - case "Temporal.PlainDate": - case "Temporal.PlainTime": - case "Temporal.PlainDateTime": - case "Temporal.PlainMonthDay": - case "Temporal.PlainYearMonth": - case "Temporal.ZonedDateTime": - str = `["${type}",${stringify_string(thing.toString())}]`; - break; - default: - if (!is_plain_object$1(thing)) { - throw new DevalueError(`Cannot stringify arbitrary non-POJOs`, keys, thing, value); - } - if (enumerable_symbols(thing).length > 0) { - throw new DevalueError(`Cannot stringify POJOs with symbolic keys`, keys, thing, value); - } - if (Object.getPrototypeOf(thing) === null) { - str = '["null"'; - for (const key of Object.keys(thing)) { - if (key === "__proto__") { - throw new DevalueError( - `Cannot stringify objects with __proto__ keys`, - keys, - thing, - value - ); - } - keys.push(stringify_key(key)); - str += `,${stringify_string(key)},${flatten(thing[key])}`; - keys.pop(); - } - str += "]"; - } else { - str = "{"; - let started = false; - for (const key of Object.keys(thing)) { - if (key === "__proto__") { - throw new DevalueError( - `Cannot stringify objects with __proto__ keys`, - keys, - thing, - value - ); - } - if (started) str += ","; - started = true; - keys.push(stringify_key(key)); - str += `${stringify_string(key)}:${flatten(thing[key])}`; - keys.pop(); - } - str += "}"; - } - } - } - stringified[index2] = str; - return index2; - } - const index = flatten(value); - if (index < 0) return `${index}`; - return `[${stringified.join(",")}]`; -} -function stringify_primitive(thing) { - const type = typeof thing; - if (type === "string") return stringify_string(thing); - if (thing === void 0) return UNDEFINED.toString(); - if (thing === 0 && 1 / thing < 0) return NEGATIVE_ZERO.toString(); - if (type === "bigint") return `["BigInt","${thing}"]`; - return String(thing); -} -function set_nested_value(object, path_string, value) { - if (path_string.startsWith("n:")) { - path_string = path_string.slice(2); - value = value === "" ? void 0 : parseFloat(value); - } else if (path_string.startsWith("b:")) { - path_string = path_string.slice(2); - value = value === "on"; - } - deep_set(object, split_path(path_string), value); -} -function convert_formdata(data) { - const result = {}; - for (let key of data.keys()) { - const is_array = key.endsWith("[]"); - let values = data.getAll(key); - if (is_array) key = key.slice(0, -2); - if (values.length > 1 && !is_array) { - throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`); - } - values = values.filter( - (entry) => typeof entry === "string" || entry.name !== "" || entry.size > 0 - ); - if (key.startsWith("n:")) { - key = key.slice(2); - values = values.map((v) => v === "" ? void 0 : parseFloat( - /** @type {string} */ - v - )); - } else if (key.startsWith("b:")) { - key = key.slice(2); - values = values.map((v) => v === "on"); - } - set_nested_value(result, key, is_array ? values : values[0]); - } - return result; -} -const BINARY_FORM_CONTENT_TYPE = "application/x-sveltekit-formdata"; -const BINARY_FORM_VERSION = 0; -const HEADER_BYTES = 1 + 4 + 2; -async function deserialize_binary_form(request) { - if (request.headers.get("content-type") !== BINARY_FORM_CONTENT_TYPE) { - const form_data = await request.formData(); - return { data: convert_formdata(form_data), meta: {}, form_data }; - } - if (!request.body) { - throw deserialize_error("no body"); - } - const content_length = parseInt(request.headers.get("content-length") ?? ""); - if (Number.isNaN(content_length)) { - throw deserialize_error("invalid Content-Length header"); - } - const reader = request.body.getReader(); - const chunks = []; - function get_chunk(index) { - if (index in chunks) return chunks[index]; - let i = chunks.length; - while (i <= index) { - chunks[i] = reader.read().then((chunk) => chunk.value); - i++; - } - return chunks[index]; - } - async function get_buffer(offset, length) { - let start_chunk; - let chunk_start = 0; - let chunk_index; - for (chunk_index = 0; ; chunk_index++) { - const chunk = await get_chunk(chunk_index); - if (!chunk) return null; - const chunk_end = chunk_start + chunk.byteLength; - if (offset >= chunk_start && offset < chunk_end) { - start_chunk = chunk; - break; - } - chunk_start = chunk_end; - } - if (offset + length <= chunk_start + start_chunk.byteLength) { - return start_chunk.subarray(offset - chunk_start, offset + length - chunk_start); - } - const chunks2 = [start_chunk.subarray(offset - chunk_start)]; - let cursor = start_chunk.byteLength - offset + chunk_start; - while (cursor < length) { - chunk_index++; - let chunk = await get_chunk(chunk_index); - if (!chunk) return null; - if (chunk.byteLength > length - cursor) { - chunk = chunk.subarray(0, length - cursor); - } - chunks2.push(chunk); - cursor += chunk.byteLength; - } - const buffer2 = new Uint8Array(length); - cursor = 0; - for (const chunk of chunks2) { - buffer2.set(chunk, cursor); - cursor += chunk.byteLength; - } - return buffer2; - } - const header = await get_buffer(0, HEADER_BYTES); - if (!header) throw deserialize_error("too short"); - if (header[0] !== BINARY_FORM_VERSION) { - throw deserialize_error(`got version ${header[0]}, expected version ${BINARY_FORM_VERSION}`); - } - const header_view = new DataView(header.buffer, header.byteOffset, header.byteLength); - const data_length = header_view.getUint32(1, true); - if (HEADER_BYTES + data_length > content_length) { - throw deserialize_error("data overflow"); - } - const file_offsets_length = header_view.getUint16(5, true); - if (HEADER_BYTES + data_length + file_offsets_length > content_length) { - throw deserialize_error("file offset table overflow"); - } - const data_buffer = await get_buffer(HEADER_BYTES, data_length); - if (!data_buffer) throw deserialize_error("data too short"); - let file_offsets; - let files_start_offset; - if (file_offsets_length > 0) { - const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length); - if (!file_offsets_buffer) throw deserialize_error("file offset table too short"); - const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer)); - if (!Array.isArray(parsed_offsets) || parsed_offsets.some((n) => typeof n !== "number" || !Number.isInteger(n) || n < 0)) { - throw deserialize_error("invalid file offset table"); - } - file_offsets = /** @type {Array} */ - parsed_offsets; - files_start_offset = HEADER_BYTES + data_length + file_offsets_length; - } - const file_spans = []; - const [data, meta] = parse(text_decoder.decode(data_buffer), { - File: ([name, type, size, last_modified, index]) => { - if (typeof name !== "string" || typeof type !== "string" || typeof size !== "number" || typeof last_modified !== "number" || typeof index !== "number") { - throw deserialize_error("invalid file metadata"); - } - let offset = file_offsets[index]; - if (offset === void 0) { - throw deserialize_error("duplicate file offset table index"); - } - file_offsets[index] = void 0; - offset += files_start_offset; - if (offset + size > content_length) { - throw deserialize_error("file data overflow"); - } - file_spans.push({ offset, size }); - return new Proxy(new LazyFile(name, type, size, last_modified, get_chunk, offset), { - getPrototypeOf() { - return File.prototype; - } - }); - } - }); - file_spans.sort((a, b) => a.offset - b.offset || a.size - b.size); - for (let i = 1; i < file_spans.length; i++) { - const previous = file_spans[i - 1]; - const current = file_spans[i]; - const previous_end = previous.offset + previous.size; - if (previous_end < current.offset) { - throw deserialize_error("gaps in file data"); - } - if (previous_end > current.offset) { - throw deserialize_error("overlapping file data"); - } - } - void (async () => { - let has_more = true; - while (has_more) { - const chunk = await get_chunk(chunks.length); - has_more = !!chunk; - } - })(); - return { data, meta, form_data: null }; -} -function deserialize_error(message) { - return new SvelteKitError(400, "Bad Request", `Could not deserialize binary form: ${message}`); -} -class LazyFile { - /** @type {(index: number) => Promise | undefined>} */ - #get_chunk; - /** @type {number} */ - #offset; - /** - * @param {string} name - * @param {string} type - * @param {number} size - * @param {number} last_modified - * @param {(index: number) => Promise | undefined>} get_chunk - * @param {number} offset - */ - constructor(name, type, size, last_modified, get_chunk, offset) { - this.name = name; - this.type = type; - this.size = size; - this.lastModified = last_modified; - this.webkitRelativePath = ""; - this.#get_chunk = get_chunk; - this.#offset = offset; - this.arrayBuffer = this.arrayBuffer.bind(this); - this.bytes = this.bytes.bind(this); - this.slice = this.slice.bind(this); - this.stream = this.stream.bind(this); - this.text = this.text.bind(this); - } - /** @type {ArrayBuffer | undefined} */ - #buffer; - async arrayBuffer() { - this.#buffer ??= await new Response(this.stream()).arrayBuffer(); - return this.#buffer; - } - async bytes() { - return new Uint8Array(await this.arrayBuffer()); - } - /** - * @param {number=} start - * @param {number=} end - * @param {string=} contentType - */ - slice(start = 0, end = this.size, contentType = this.type) { - if (start < 0) { - start = Math.max(this.size + start, 0); - } else { - start = Math.min(start, this.size); - } - if (end < 0) { - end = Math.max(this.size + end, 0); - } else { - end = Math.min(end, this.size); - } - const size = Math.max(end - start, 0); - const file = new LazyFile( - this.name, - contentType, - size, - this.lastModified, - this.#get_chunk, - this.#offset + start - ); - return file; - } - stream() { - let cursor = 0; - let chunk_index = 0; - return new ReadableStream({ - start: async (controller) => { - let chunk_start = 0; - let start_chunk; - for (chunk_index = 0; ; chunk_index++) { - const chunk = await this.#get_chunk(chunk_index); - if (!chunk) return null; - const chunk_end = chunk_start + chunk.byteLength; - if (this.#offset >= chunk_start && this.#offset < chunk_end) { - start_chunk = chunk; - break; - } - chunk_start = chunk_end; - } - if (this.#offset + this.size <= chunk_start + start_chunk.byteLength) { - controller.enqueue( - start_chunk.subarray(this.#offset - chunk_start, this.#offset + this.size - chunk_start) - ); - controller.close(); - } else { - controller.enqueue(start_chunk.subarray(this.#offset - chunk_start)); - cursor = start_chunk.byteLength - this.#offset + chunk_start; - } - }, - pull: async (controller) => { - chunk_index++; - let chunk = await this.#get_chunk(chunk_index); - if (!chunk) { - controller.error("incomplete file data"); - controller.close(); - return; - } - if (chunk.byteLength > this.size - cursor) { - chunk = chunk.subarray(0, this.size - cursor); - } - controller.enqueue(chunk); - cursor += chunk.byteLength; - if (cursor >= this.size) { - controller.close(); - } - } - }); - } - async text() { - return text_decoder.decode(await this.arrayBuffer()); - } -} -const path_regex = /^[a-zA-Z_$]\w*(\.[a-zA-Z_$]\w*|\[\d+\])*$/; -function split_path(path) { - if (!path_regex.test(path)) { - throw new Error(`Invalid path ${path}`); - } - return path.split(/\.|\[|\]/).filter(Boolean); -} -function check_prototype_pollution(key) { - if (key === "__proto__" || key === "constructor" || key === "prototype") { - throw new Error( - `Invalid key "${key}"` - ); - } -} -function deep_set(object, keys, value) { - let current = object; - for (let i = 0; i < keys.length - 1; i += 1) { - const key = keys[i]; - check_prototype_pollution(key); - const is_array = /^\d+$/.test(keys[i + 1]); - const exists = Object.hasOwn(current, key); - const inner = current[key]; - if (exists && is_array !== Array.isArray(inner)) { - throw new Error(`Invalid array key ${keys[i + 1]}`); - } - if (!exists) { - current[key] = is_array ? [] : {}; - } - current = current[key]; - } - const final_key = keys[keys.length - 1]; - check_prototype_pollution(final_key); - current[final_key] = value; -} -function normalize_issue(issue, server = false) { - const normalized = { name: "", path: [], message: issue.message, server }; - if (issue.path !== void 0) { - let name = ""; - for (const segment of issue.path) { - const key = ( - /** @type {string | number} */ - typeof segment === "object" ? segment.key : segment - ); - normalized.path.push(key); - if (typeof key === "number") { - name += `[${key}]`; - } else if (typeof key === "string") { - name += name === "" ? key : "." + key; - } - } - normalized.name = name; - } - return normalized; -} -function flatten_issues(issues) { - const result = {}; - for (const issue of issues) { - (result.$ ??= []).push(issue); - let name = ""; - if (issue.path !== void 0) { - for (const key of issue.path) { - if (typeof key === "number") { - name += `[${key}]`; - } else if (typeof key === "string") { - name += name === "" ? key : "." + key; - } - (result[name] ??= []).push(issue); - } - } - } - return result; -} -function deep_get(object, path) { - let current = object; - for (const key of path) { - if (current == null || typeof current !== "object") { - return current; - } - current = current[key]; - } - return current; -} -function create_field_proxy(target, get_input, set_input, get_issues, path = []) { - const get_value = () => { - return deep_get(get_input(), path); - }; - return new Proxy(target, { - get(target2, prop) { - if (typeof prop === "symbol") return target2[prop]; - if (/^\d+$/.test(prop)) { - return create_field_proxy({}, get_input, set_input, get_issues, [ - ...path, - parseInt(prop, 10) - ]); - } - const key = build_path_string(path); - if (prop === "set") { - const set_func = function(newValue) { - set_input(path, newValue); - return newValue; - }; - return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]); - } - if (prop === "value") { - return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]); - } - if (prop === "issues" || prop === "allIssues") { - const issues_func = () => { - const all_issues = get_issues()[key === "" ? "$" : key]; - if (prop === "allIssues") { - return all_issues?.map((issue) => ({ - path: issue.path, - message: issue.message - })); - } - return all_issues?.filter((issue) => issue.name === key)?.map((issue) => ({ - path: issue.path, - message: issue.message - })); - }; - return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]); - } - if (prop === "as") { - const as_func = (type, input_value) => { - const is_array = type === "file multiple" || type === "select multiple" || type === "checkbox" && typeof input_value === "string"; - const prefix = type === "number" || type === "range" ? "n:" : type === "checkbox" && !is_array ? "b:" : ""; - const base_props = { - name: prefix + key + (is_array ? "[]" : ""), - get "aria-invalid"() { - const issues = get_issues(); - return key in issues ? "true" : void 0; - } - }; - if (type !== "text" && type !== "select" && type !== "select multiple") { - base_props.type = type === "file multiple" ? "file" : type; - } - if (type === "submit" || type === "hidden") { - return Object.defineProperties(base_props, { - value: { value: input_value, enumerable: true } - }); - } - if (type === "select" || type === "select multiple") { - return Object.defineProperties(base_props, { - multiple: { value: is_array, enumerable: true }, - value: { - enumerable: true, - get() { - return input_value !== void 0 ? input_value : get_value(); - } - } - }); - } - if (type === "checkbox" || type === "radio") { - return Object.defineProperties(base_props, { - value: { value: input_value ?? "on", enumerable: true }, - checked: { - enumerable: true, - get() { - const value = get_value(); - if (type === "radio") { - return value === input_value; - } - if (is_array) { - return (value ?? []).includes(input_value); - } - return value; - } - } - }); - } - if (type === "file" || type === "file multiple") { - return Object.defineProperties(base_props, { - multiple: { value: is_array, enumerable: true }, - files: { - enumerable: true, - get() { - const value = get_value(); - if (value instanceof File) { - if (typeof DataTransfer !== "undefined") { - const fileList = new DataTransfer(); - fileList.items.add(value); - return fileList.files; - } - return { 0: value, length: 1 }; - } - if (Array.isArray(value) && value.every((f) => f instanceof File)) { - if (typeof DataTransfer !== "undefined") { - const fileList = new DataTransfer(); - value.forEach((file) => fileList.items.add(file)); - return fileList.files; - } - const fileListLike = { length: value.length }; - value.forEach((file, index) => { - fileListLike[index] = file; - }); - return fileListLike; - } - return null; - } - } - }); - } - return Object.defineProperties(base_props, { - value: { - enumerable: true, - get() { - const value = input_value !== void 0 ? input_value : get_value(); - return value != null ? String(value) : ""; - } - } - }); - }; - return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, "as"]); - } - return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]); - } - }); -} -function build_path_string(path) { - let result = ""; - for (const segment of path) { - if (typeof segment === "number") { - result += `[${segment}]`; - } else { - result += result === "" ? segment : "." + segment; - } - } - return result; -} -function negotiate(accept, types) { - const parts = []; - accept.split(",").forEach((str, i) => { - const match = /([^/ \t]+)\/([^; \t]+)[ \t]*(?:;[ \t]*q=([0-9.]+))?/.exec(str); - if (match) { - const [, type, subtype, q = "1"] = match; - parts.push({ type, subtype, q: +q, i }); - } - }); - parts.sort((a, b) => { - if (a.q !== b.q) { - return b.q - a.q; - } - if (a.subtype === "*" !== (b.subtype === "*")) { - return a.subtype === "*" ? 1 : -1; - } - if (a.type === "*" !== (b.type === "*")) { - return a.type === "*" ? 1 : -1; - } - return a.i - b.i; - }); - let accepted; - let min_priority = Infinity; - for (const mimetype of types) { - const [type, subtype] = mimetype.split("/"); - const priority = parts.findIndex( - (part) => (part.type === type || part.type === "*") && (part.subtype === subtype || part.subtype === "*") - ); - if (priority !== -1 && priority < min_priority) { - accepted = mimetype; - min_priority = priority; - } - } - return accepted; -} -function is_content_type(request, ...types) { - const type = request.headers.get("content-type")?.split(";", 1)[0].trim() ?? ""; - return types.includes(type.toLowerCase()); -} -function is_form_content_type(request) { - return is_content_type( - request, - "application/x-www-form-urlencoded", - "multipart/form-data", - "text/plain", - BINARY_FORM_CONTENT_TYPE - ); -} -function coalesce_to_error(err) { - return err instanceof Error || err && /** @type {any} */ - err.name && /** @type {any} */ - err.message ? ( - /** @type {Error} */ - err - ) : new Error(JSON.stringify(err)); -} -function normalize_error(error) { - return ( - /** @type {import('../exports/internal/index.js').Redirect | HttpError | SvelteKitError | Error} */ - error - ); -} -function get_status(error) { - return error instanceof HttpError || error instanceof SvelteKitError ? error.status : 500; -} -function get_message(error) { - return error instanceof SvelteKitError ? error.text : "Internal Error"; -} -const escape_html_attr_dict = { - "&": "&", - '"': """ - // Svelte also escapes < because the escape function could be called inside a `noscript` there - // https://github.com/sveltejs/svelte/security/advisories/GHSA-8266-84wp-wv5c - // However, that doesn't apply in SvelteKit -}; -const escape_html_dict = { - "&": "&", - "<": "<" -}; -const surrogates = ( - // high surrogate without paired low surrogate - "[\\ud800-\\udbff](?![\\udc00-\\udfff])|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\udc00-\\udfff]" -); -const escape_html_attr_regex = new RegExp( - `[${Object.keys(escape_html_attr_dict).join("")}]|` + surrogates, - "g" -); -const escape_html_regex = new RegExp( - `[${Object.keys(escape_html_dict).join("")}]|` + surrogates, - "g" -); -function escape_html(str, is_attr) { - const dict = is_attr ? escape_html_attr_dict : escape_html_dict; - const escaped_str = str.replace(is_attr ? escape_html_attr_regex : escape_html_regex, (match) => { - if (match.length === 2) { - return match; - } - return dict[match] ?? `&#${match.charCodeAt(0)};`; - }); - return escaped_str; -} -function method_not_allowed(mod, method) { - return text(`${method} method not allowed`, { - status: 405, - headers: { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: allowed_methods(mod).join(", ") - } - }); -} -function allowed_methods(mod) { - const allowed = ENDPOINT_METHODS.filter((method) => method in mod); - if ("GET" in mod && !("HEAD" in mod)) { - allowed.push("HEAD"); - } - return allowed; -} -function get_global_name(options) { - return `__sveltekit_${options.version_hash}`; -} -function static_error_page(options, status, message) { - let page = options.templates.error({ status, message: escape_html(message) }); - return text(page, { - headers: { "content-type": "text/html; charset=utf-8" }, - status - }); -} -async function handle_fatal_error(event, state, options, error) { - error = error instanceof HttpError ? error : coalesce_to_error(error); - const status = get_status(error); - const body = await handle_error_and_jsonify(event, state, options, error); - const type = negotiate(event.request.headers.get("accept") || "text/html", [ - "application/json", - "text/html" - ]); - if (event.isDataRequest || type === "application/json") { - return json(body, { - status - }); - } - return static_error_page(options, status, body.message); -} -async function handle_error_and_jsonify(event, state, options, error) { - if (error instanceof HttpError) { - return { message: "Unknown Error", ...error.body }; - } - const status = get_status(error); - const message = get_message(error); - return await with_request_store( - { event, state }, - () => options.hooks.handleError({ error, event, status, message }) - ) ?? { message }; -} -function redirect_response(status, location) { - const response = new Response(void 0, { - status, - headers: { location } - }); - return response; -} -function clarify_devalue_error(event, error) { - if (error.path) { - return `Data returned from \`load\` while rendering ${event.route.id} is not serializable: ${error.message} (${error.path}). If you need to serialize/deserialize custom types, use transport hooks: https://svelte.dev/docs/kit/hooks#Universal-hooks-transport.`; - } - if (error.path === "") { - return `Data returned from \`load\` while rendering ${event.route.id} is not a plain object`; - } - return error.message; -} -function serialize_uses(node) { - const uses = {}; - if (node.uses && node.uses.dependencies.size > 0) { - uses.dependencies = Array.from(node.uses.dependencies); - } - if (node.uses && node.uses.search_params.size > 0) { - uses.search_params = Array.from(node.uses.search_params); - } - if (node.uses && node.uses.params.size > 0) { - uses.params = Array.from(node.uses.params); - } - if (node.uses?.parent) uses.parent = 1; - if (node.uses?.route) uses.route = 1; - if (node.uses?.url) uses.url = 1; - return uses; -} -function has_prerendered_path(manifest, pathname) { - return manifest._.prerendered_routes.has(pathname) || pathname.at(-1) === "/" && manifest._.prerendered_routes.has(pathname.slice(0, -1)); -} -function format_server_error(status, error, event) { - const formatted_text = ` -\x1B[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1B[0m`; - if (status === 404) { - return formatted_text; - } - return `${formatted_text} -${error.stack}`; -} -function get_node_type(node_id) { - const parts = node_id?.split("/"); - const filename = parts?.at(-1); - if (!filename) return "unknown"; - const dot_parts = filename.split("."); - return dot_parts.slice(0, -1).join("."); -} -function hydratable(key, fn) { - { - experimental_async_required(); - } - const { hydratable: hydratable2 } = get_render_context(); - let entry = hydratable2.lookup.get(key); - if (entry !== void 0) { - return ( - /** @type {T} */ - entry.value - ); - } - const value = fn(); - entry = encode(key, value, hydratable2.unresolved_promises); - hydratable2.lookup.set(key, entry); - return value; -} -function encode(key, value, unresolved) { - const entry = { value, serialized: "" }; - let uid = 1; - entry.serialized = uneval(entry.value, (value2, uneval2) => { - if (is_promise(value2)) { - const placeholder = `"${uid++}"`; - const p = value2.then((v) => { - entry.serialized = entry.serialized.replace(placeholder, `r(${uneval2(v)})`); - }).catch( - (devalue_error) => hydratable_serialization_failed( - key, - serialization_stack(entry.stack, devalue_error?.stack) - ) - ); - p.catch(() => { - }).finally(() => unresolved?.delete(p)); - (entry.promises ??= []).push(p); - return placeholder; - } - }); - return entry; -} -function is_promise(value) { - return Object.prototype.toString.call(value) === "[object Promise]"; -} -function serialization_stack(root_stack, uneval_stack) { - let out = ""; - if (root_stack) { - out += root_stack + "\n"; - } - if (uneval_stack) { - out += "Caused by:\n" + uneval_stack + "\n"; - } - return out || ""; -} -const INVALIDATED_PARAM = "x-sveltekit-invalidated"; -const TRAILING_SLASH_PARAM = "x-sveltekit-trailing-slash"; -function stringify(data, transport) { - const encoders = Object.fromEntries(Object.entries(transport).map(([k, v]) => [k, v.encode])); - return stringify$1(data, encoders); -} -const object_proto_names = /* @__PURE__ */ Object.getOwnPropertyNames(Object.prototype).sort().join("\0"); -function is_plain_object(thing) { - if (typeof thing !== "object" || thing === null) return false; - const proto = Object.getPrototypeOf(thing); - return proto === Object.prototype || proto === null || Object.getPrototypeOf(proto) === null || Object.getOwnPropertyNames(proto).sort().join("\0") === object_proto_names; -} -function to_sorted(value, clones) { - const clone = Object.getPrototypeOf(value) === null ? /* @__PURE__ */ Object.create(null) : {}; - clones.set(value, clone); - Object.defineProperty(clone, remote_arg_marker, { value: true }); - for (const key of Object.keys(value).sort()) { - const property = value[key]; - Object.defineProperty(clone, key, { - value: clones.get(property) ?? property, - enumerable: true, - configurable: true, - writable: true - }); - } - return clone; -} -const remote_object = "__skrao"; -const remote_map = "__skram"; -const remote_set = "__skras"; -const remote_regex_guard = "__skrag"; -const remote_arg_marker = Symbol(remote_object); -function create_remote_arg_reducers(transport, sort, remote_arg_clones) { - const remote_fns_reducers = { - [remote_regex_guard]: ( - /** @type {(value: unknown) => void} */ - (value) => { - if (value instanceof RegExp) { - throw new Error("Regular expressions are not valid remote function arguments"); - } - } - ) - }; - if (sort) { - remote_fns_reducers[remote_map] = (value) => { - if (!(value instanceof Map)) { - return; - } - const entries = []; - for (const [key, val] of value) { - entries.push([stringify2(key), stringify2(val)]); - } - return entries.sort(([a1, a2], [b1, b2]) => { - if (a1 < b1) return -1; - if (a1 > b1) return 1; - if (a2 < b2) return -1; - if (a2 > b2) return 1; - return 0; - }); - }; - remote_fns_reducers[remote_set] = (value) => { - if (!(value instanceof Set)) { - return; - } - const items = []; - for (const item of value) { - items.push(stringify2(item)); - } - items.sort(); - return items; - }; - remote_fns_reducers[remote_object] = (value) => { - if (!is_plain_object(value)) { - return; - } - if (Object.hasOwn(value, remote_arg_marker)) { - return; - } - if (remote_arg_clones.has(value)) { - return remote_arg_clones.get(value); - } - return to_sorted(value, remote_arg_clones); - }; - } - const user_reducers = Object.fromEntries( - Object.entries(transport).map(([k, v]) => [k, v.encode]) - ); - const all_reducers = { ...user_reducers, ...remote_fns_reducers }; - const stringify2 = (value) => stringify$1(value, all_reducers); - return all_reducers; -} -function create_remote_arg_revivers(transport) { - const remote_fns_revivers = { - /** @type {(value: unknown) => unknown} */ - [remote_object]: (value) => value, - /** @type {(value: unknown) => Map} */ - [remote_map]: (value) => { - if (!Array.isArray(value)) { - throw new Error("Invalid data for Map reviver"); - } - const map = /* @__PURE__ */ new Map(); - for (const item of value) { - if (!Array.isArray(item) || item.length !== 2 || typeof item[0] !== "string" || typeof item[1] !== "string") { - throw new Error("Invalid data for Map reviver"); - } - const [key, val] = item; - map.set(parse$1(key), parse$1(val)); - } - return map; - }, - /** @type {(value: unknown) => Set} */ - [remote_set]: (value) => { - if (!Array.isArray(value)) { - throw new Error("Invalid data for Set reviver"); - } - const set = /* @__PURE__ */ new Set(); - for (const item of value) { - if (typeof item !== "string") { - throw new Error("Invalid data for Set reviver"); - } - set.add(parse$1(item)); - } - return set; - } - }; - const user_revivers = Object.fromEntries( - Object.entries(transport).map(([k, v]) => [k, v.decode]) - ); - const all_revivers = { ...user_revivers, ...remote_fns_revivers }; - const parse$1 = (data) => parse(data, all_revivers); - return all_revivers; -} -function stringify_remote_arg(value, transport, sort = true) { - if (value === void 0) return ""; - const json_string = stringify$1( - value, - create_remote_arg_reducers(transport, sort, /* @__PURE__ */ new Map()) - ); - const bytes = new TextEncoder().encode(json_string); - return base64_encode(bytes).replaceAll("=", "").replaceAll("+", "-").replaceAll("/", "_"); -} -function parse_remote_arg(string, transport) { - if (!string) return void 0; - const json_string = text_decoder.decode( - // no need to add back `=` characters, atob can handle it - base64_decode(string.replaceAll("-", "+").replaceAll("_", "/")) - ); - return parse(json_string, create_remote_arg_revivers(transport)); -} -function create_remote_key(id, payload) { - return id + "/" + payload; -} -function split_remote_key(key) { - const i = key.lastIndexOf("/"); - if (i === -1) { - throw new Error(`Invalid remote key: ${key}`); - } - return { - id: key.slice(0, i), - payload: key.slice(i + 1) - }; -} -function unfriendly_hydratable(key, fn) { - if (!hydratable) { - throw new Error("Remote functions require Svelte 5.44.0 or later"); - } - return hydratable(key, fn); -} -export { - normalize_issue as A, - set_nested_value as B, - flatten_issues as C, - deep_set as D, - ENDPOINT_METHODS as E, - INVALIDATED_PARAM as I, - MUTATIVE_METHODS as M, - PAGE_METHODS as P, - TRAILING_SLASH_PARAM as T, - normalize_error as a, - get_global_name as b, - serialize_uses as c, - clarify_devalue_error as d, - get_node_type as e, - escape_html as f, - get_status as g, - handle_error_and_jsonify as h, - is_form_content_type as i, - create_remote_key as j, - static_error_page as k, - stringify as l, - method_not_allowed as m, - negotiate as n, - deserialize_binary_form as o, - parse_remote_arg as p, - split_remote_key as q, - redirect_response as r, - stringify$1 as s, - has_prerendered_path as t, - handle_fatal_error as u, - format_server_error as v, - stringify_remote_arg as w, - unfriendly_hydratable as x, - parse as y, - create_field_proxy as z -}; - -``` - ---- - -## .svelte-kit/output/server/chunks/state.svelte.js - -```js -import { n as noop } from "./attributes.js"; -import "./exports.js"; -import "@sveltejs/kit/internal/server"; -import "./root.js"; -const is_legacy = noop.toString().includes("$$") || /function \w+\(\) \{\}/.test(noop.toString()); -const placeholder_url = "a:"; -if (is_legacy) { - ({ - data: {}, - form: null, - error: null, - params: {}, - route: { id: null }, - state: {}, - status: -1, - url: new URL(placeholder_url) - }); -} - -``` - ---- - -## .svelte-kit/output/server/chunks/utils.js - -```js -const text_encoder = new TextEncoder(); -const text_decoder = new TextDecoder(); -function base64_encode(bytes) { - if (globalThis.Buffer) { - return globalThis.Buffer.from(bytes).toString("base64"); - } - let binary = ""; - for (let i = 0; i < bytes.length; i++) { - binary += String.fromCharCode(bytes[i]); - } - return btoa(binary); -} -function base64_decode(encoded) { - if (globalThis.Buffer) { - const buffer = globalThis.Buffer.from(encoded, "base64"); - return new Uint8Array(buffer); - } - const binary = atob(encoded); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) { - bytes[i] = binary.charCodeAt(i); - } - return bytes; -} -export { - text_encoder as a, - base64_encode as b, - base64_decode as c, - text_decoder as t -}; - -``` - ---- - -## .svelte-kit/output/server/entries/endpoints/api/auth/login/_server.ts.js - -```js -import { json } from "@sveltejs/kit"; -import { createInstance } from "@amplitude/analytics-node"; -import { p as public_env } from "../../../../../chunks/shared-server.js"; -let amplitudeClient = null; -function getAmplitudeClient() { - if (!amplitudeClient) { - amplitudeClient = createInstance(); - amplitudeClient.init(public_env.PUBLIC_AMPLITUDE_API_KEY ?? ""); - } - return amplitudeClient; -} -const users = /* @__PURE__ */ new Map(); -const POST = async ({ request }) => { - const { username, password } = await request.json(); - if (!username || !password) { - return json({ error: "Username and password required" }, { status: 400 }); - } - let user = users.get(username); - const isNewUser = !user; - if (!user) { - user = { username, burritoConsiderations: 0 }; - users.set(username, user); - } - const amplitude = getAmplitudeClient(); - amplitude.track("server_login", { isNewUser, source: "api" }, { user_id: username }); - await amplitude.flush(); - return json({ success: true, user }); -}; -export { - POST -}; - -``` - ---- - -## .svelte-kit/output/server/entries/fallbacks/error.svelte.js - -```js -import { g as getContext, e as escape_html } from "../../chunks/attributes.js"; -import "../../chunks/state.svelte.js"; -import "@sveltejs/kit/internal"; -import "../../chunks/exports.js"; -import "../../chunks/utils.js"; -import { w as writable } from "../../chunks/index.js"; -import "@sveltejs/kit/internal/server"; -import "../../chunks/root.js"; -function create_updated_store() { - const { set, subscribe } = writable(false); - { - return { - subscribe, - // eslint-disable-next-line @typescript-eslint/require-await - check: async () => false - }; - } -} -const stores = { - updated: /* @__PURE__ */ create_updated_store() -}; -({ - check: stores.updated.check -}); -function context() { - return getContext("__request__"); -} -const page$1 = { - get error() { - return context().page.error; - }, - get status() { - return context().page.status; - } -}; -const page = page$1; -function Error$1($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - $$renderer2.push(`

${escape_html(page.status)}

${escape_html(page.error?.message)}

`); - }); -} -export { - Error$1 as default -}; - -``` - ---- - -## .svelte-kit/output/server/entries/hooks.server.js - -```js -const handle = async ({ event, resolve }) => { - return resolve(event); -}; -export { - handle -}; - -``` - ---- - -## .svelte-kit/output/server/entries/pages/_layout.svelte.js - -```js -import { h as head } from "../../chunks/renderer.js"; -import { g as getAuthContext, s as setAuthContext, A as AuthState } from "../../chunks/auth.svelte.js"; -import { e as escape_html } from "../../chunks/attributes.js"; -function Header($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - const auth = getAuthContext(); - $$renderer2.push(`
`); - if (auth.user) { - $$renderer2.push(""); - $$renderer2.push(`Welcome, ${escape_html(auth.user.username)} `); - } else { - $$renderer2.push(""); - } - $$renderer2.push(`
`); - }); -} -function _layout($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - let { children } = $$props; - const auth = new AuthState(); - setAuthContext(auth); - head("12qhfyh", $$renderer2, ($$renderer3) => { - $$renderer3.title(($$renderer4) => { - $$renderer4.push(`Burrito consideration app`); - }); - $$renderer3.push(``); - }); - Header($$renderer2); - $$renderer2.push(`
`); - children($$renderer2); - $$renderer2.push(`
`); - }); -} -export { - _layout as default -}; - -``` - ---- - -## .svelte-kit/output/server/entries/pages/_page.svelte.js - -```js -import { e as escape_html, a as attr } from "../../chunks/attributes.js"; -import { g as getAuthContext } from "../../chunks/auth.svelte.js"; -function _page($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - const auth = getAuthContext(); - let username = ""; - let password = ""; - $$renderer2.push(`
`); - if (auth.user) { - $$renderer2.push(""); - $$renderer2.push(`

Welcome back, ${escape_html(auth.user.username)}!

You are now logged in. Check out the navigation to explore features.

`); - } else { - $$renderer2.push(""); - $$renderer2.push(`

Welcome to Burrito consideration app

Sign in to start considering burritos.

`); - { - $$renderer2.push(""); - } - $$renderer2.push(`

Enter any username and password to sign in. This is a demo app.

`); - } - $$renderer2.push(`
`); - }); -} -export { - _page as default -}; - -``` - ---- - -## .svelte-kit/output/server/entries/pages/burrito/_page.svelte.js - -```js -import { e as escape_html } from "../../../chunks/attributes.js"; -import "@sveltejs/kit/internal"; -import "../../../chunks/exports.js"; -import "../../../chunks/utils.js"; -import "@sveltejs/kit/internal/server"; -import "../../../chunks/root.js"; -import "../../../chunks/state.svelte.js"; -import { g as getAuthContext } from "../../../chunks/auth.svelte.js"; -function _page($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - const auth = getAuthContext(); - $$renderer2.push(`
`); - if (auth.user) { - $$renderer2.push(""); - $$renderer2.push(`

Burrito consideration zone

This is where you consider the infinite potential of burritos.

Current considerations: ${escape_html(auth.user.burritoConsiderations)}

`); - { - $$renderer2.push(""); - } - $$renderer2.push(`

Each consideration is tracked as an Amplitude event with custom properties.

`); - } else { - $$renderer2.push(""); - $$renderer2.push(`

Please log in to consider burritos.

`); - } - $$renderer2.push(`
`); - }); -} -export { - _page as default -}; - -``` - ---- - -## .svelte-kit/output/server/entries/pages/profile/_page.svelte.js - -```js -import { e as escape_html } from "../../../chunks/attributes.js"; -import "@sveltejs/kit/internal"; -import "../../../chunks/exports.js"; -import "../../../chunks/utils.js"; -import "@sveltejs/kit/internal/server"; -import "../../../chunks/root.js"; -import "../../../chunks/state.svelte.js"; -import { g as getAuthContext } from "../../../chunks/auth.svelte.js"; -function _page($$renderer, $$props) { - $$renderer.component(($$renderer2) => { - const auth = getAuthContext(); - $$renderer2.push(`
`); - if ( - // Redirect to home if not logged in - auth.user - ) { - $$renderer2.push(""); - $$renderer2.push(`

User profile

Your information

Username: ${escape_html(auth.user.username)}

Burrito considerations: ${escape_html(auth.user.burritoConsiderations)}

`); - } else { - $$renderer2.push(""); - $$renderer2.push(`

Please log in to view your profile.

`); - } - $$renderer2.push(`
`); - }); -} -export { - _page as default -}; - -``` - ---- - -## .svelte-kit/output/server/index.js - -```js -import { B as BROWSER } from "./chunks/errors.js"; -import { json, text, isRedirect, error } from "@sveltejs/kit"; -import { Redirect, SvelteKitError, ActionFailure, HttpError } from "@sveltejs/kit/internal"; -import { with_request_store, merge_tracing, try_get_request_store } from "@sveltejs/kit/internal/server"; -import { a as assets, b as base, c as app_dir, r as relative, o as override, d as reset } from "./chunks/environment.js"; -import { E as ENDPOINT_METHODS, P as PAGE_METHODS, n as negotiate, m as method_not_allowed, h as handle_error_and_jsonify, g as get_status, i as is_form_content_type, a as normalize_error, s as stringify, b as get_global_name, c as serialize_uses, d as clarify_devalue_error, e as get_node_type, f as escape_html, j as create_remote_key, k as static_error_page, r as redirect_response, p as parse_remote_arg, l as stringify$1, o as deserialize_binary_form, q as split_remote_key, t as has_prerendered_path, T as TRAILING_SLASH_PARAM, I as INVALIDATED_PARAM, u as handle_fatal_error, v as format_server_error } from "./chunks/shared.js"; -import { u as uneval } from "./chunks/render-context.js"; -import { m as make_trackable, d as disable_search, a as decode_params, S as SCHEME, v as validate_layout_server_exports, b as validate_layout_exports, c as validate_page_server_exports, e as validate_page_exports, n as normalize_path, r as resolve, f as decode_pathname, g as validate_server_exports } from "./chunks/exports.js"; -import { b as base64_encode, t as text_decoder, a as text_encoder } from "./chunks/utils.js"; -import { w as writable, r as readable } from "./chunks/index.js"; -import { p as public_env, s as set_private_env, a as set_public_env } from "./chunks/shared-server.js"; -import { r as read_implementation, o as options, g as get_hooks, s as set_read_implementation } from "./chunks/internal.js"; -function with_resolvers() { - let resolve2; - let reject; - const promise = new Promise((res, rej) => { - resolve2 = res; - reject = rej; - }); - return { promise, resolve: resolve2, reject }; -} -const NULL_BODY_STATUS = [101, 103, 204, 205, 304]; -const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer; -const s = JSON.stringify; -async function render_endpoint(event, event_state, mod, state) { - const method = ( - /** @type {import('types').HttpMethod} */ - event.request.method - ); - let handler = mod[method] || mod.fallback; - if (method === "HEAD" && !mod.HEAD && mod.GET) { - handler = mod.GET; - } - if (!handler) { - return method_not_allowed(mod, method); - } - const prerender = mod.prerender ?? state.prerender_default; - if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) { - throw new Error("Cannot prerender endpoints that have mutative methods"); - } - if (state.prerendering && !state.prerendering.inside_reroute && !prerender) { - if (state.depth > 0) { - throw new Error(`${event.route.id} is not prerenderable`); - } else { - return new Response(void 0, { status: 204 }); - } - } - try { - const response = await with_request_store( - { event, state: event_state }, - () => handler( - /** @type {import('@sveltejs/kit').RequestEvent>} */ - event - ) - ); - if (!(response instanceof Response)) { - throw new Error( - `Invalid response from route ${event.url.pathname}: handler should return a Response object` - ); - } - if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) { - const cloned = new Response(response.clone().body, { - status: response.status, - statusText: response.statusText, - headers: new Headers(response.headers) - }); - cloned.headers.set("x-sveltekit-prerender", String(prerender)); - if (state.prerendering.inside_reroute && prerender) { - cloned.headers.set( - "x-sveltekit-routeid", - encodeURI( - /** @type {string} */ - event.route.id - ) - ); - state.prerendering.dependencies.set(event.url.pathname, { response: cloned, body: null }); - } else { - return cloned; - } - } - return response; - } catch (e) { - if (e instanceof Redirect) { - return new Response(void 0, { - status: e.status, - headers: { location: e.location } - }); - } - throw e; - } -} -function is_endpoint_request(event) { - const { method, headers: headers2 } = event.request; - if (ENDPOINT_METHODS.includes(method) && !PAGE_METHODS.includes(method)) { - return true; - } - if (method === "POST" && headers2.get("x-sveltekit-action") === "true") return false; - const accept = event.request.headers.get("accept") ?? "*/*"; - return negotiate(accept, ["*", "text/html"]) !== "text/html"; -} -function compact(arr) { - return arr.filter( - /** @returns {val is NonNullable} */ - (val) => val != null - ); -} -const DATA_SUFFIX = "/__data.json"; -const HTML_DATA_SUFFIX = ".html__data.json"; -function has_data_suffix(pathname) { - return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX); -} -function add_data_suffix(pathname) { - if (pathname.endsWith(".html")) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX); - return pathname.replace(/\/$/, "") + DATA_SUFFIX; -} -function strip_data_suffix(pathname) { - if (pathname.endsWith(HTML_DATA_SUFFIX)) { - return pathname.slice(0, -HTML_DATA_SUFFIX.length) + ".html"; - } - return pathname.slice(0, -DATA_SUFFIX.length); -} -const ROUTE_SUFFIX = "/__route.js"; -function has_resolution_suffix(pathname) { - return pathname.endsWith(ROUTE_SUFFIX); -} -function add_resolution_suffix(pathname) { - return pathname.replace(/\/$/, "") + ROUTE_SUFFIX; -} -function strip_resolution_suffix(pathname) { - return pathname.slice(0, -ROUTE_SUFFIX.length); -} -const noop_span = { - spanContext() { - return noop_span_context; - }, - setAttribute() { - return this; - }, - setAttributes() { - return this; - }, - addEvent() { - return this; - }, - setStatus() { - return this; - }, - updateName() { - return this; - }, - end() { - return this; - }, - isRecording() { - return false; - }, - recordException() { - return this; - }, - addLink() { - return this; - }, - addLinks() { - return this; - } -}; -const noop_span_context = { - traceId: "", - spanId: "", - traceFlags: 0 -}; -async function record_span({ name, attributes, fn }) { - { - return fn(noop_span); - } -} -function is_action_json_request(event) { - const accept = negotiate(event.request.headers.get("accept") ?? "*/*", [ - "application/json", - "text/html" - ]); - return accept === "application/json" && event.request.method === "POST"; -} -async function handle_action_json_request(event, event_state, options2, server) { - const actions = server?.actions; - if (!actions) { - const no_actions_error = new SvelteKitError( - 405, - "Method Not Allowed", - `POST method not allowed. No form actions exist for ${"this page"}` - ); - return action_json( - { - type: "error", - error: await handle_error_and_jsonify(event, event_state, options2, no_actions_error) - }, - { - status: no_actions_error.status, - headers: { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: "GET" - } - } - ); - } - check_named_default_separate(actions); - try { - const data = await call_action(event, event_state, actions); - if (BROWSER) ; - if (data instanceof ActionFailure) { - return action_json({ - type: "failure", - status: data.status, - // @ts-expect-error we assign a string to what is supposed to be an object. That's ok - // because we don't use the object outside, and this way we have better code navigation - // through knowing where the related interface is used. - data: stringify_action_response( - data.data, - /** @type {string} */ - event.route.id, - options2.hooks.transport - ) - }); - } else { - return action_json({ - type: "success", - status: data ? 200 : 204, - // @ts-expect-error see comment above - data: stringify_action_response( - data, - /** @type {string} */ - event.route.id, - options2.hooks.transport - ) - }); - } - } catch (e) { - const err = normalize_error(e); - if (err instanceof Redirect) { - return action_json_redirect(err); - } - return action_json( - { - type: "error", - error: await handle_error_and_jsonify( - event, - event_state, - options2, - check_incorrect_fail_use(err) - ) - }, - { - status: get_status(err) - } - ); - } -} -function check_incorrect_fail_use(error2) { - return error2 instanceof ActionFailure ? new Error('Cannot "throw fail()". Use "return fail()"') : error2; -} -function action_json_redirect(redirect) { - return action_json({ - type: "redirect", - status: redirect.status, - location: redirect.location - }); -} -function action_json(data, init2) { - return json(data, init2); -} -function is_action_request(event) { - return event.request.method === "POST"; -} -async function handle_action_request(event, event_state, server) { - const actions = server?.actions; - if (!actions) { - event.setHeaders({ - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: "GET" - }); - return { - type: "error", - error: new SvelteKitError( - 405, - "Method Not Allowed", - `POST method not allowed. No form actions exist for ${"this page"}` - ) - }; - } - check_named_default_separate(actions); - try { - const data = await call_action(event, event_state, actions); - if (BROWSER) ; - if (data instanceof ActionFailure) { - return { - type: "failure", - status: data.status, - data: data.data - }; - } else { - return { - type: "success", - status: 200, - // @ts-expect-error this will be removed upon serialization, so `undefined` is the same as omission - data - }; - } - } catch (e) { - const err = normalize_error(e); - if (err instanceof Redirect) { - return { - type: "redirect", - status: err.status, - location: err.location - }; - } - return { - type: "error", - error: check_incorrect_fail_use(err) - }; - } -} -function check_named_default_separate(actions) { - if (actions.default && Object.keys(actions).length > 1) { - throw new Error( - "When using named actions, the default action cannot be used. See the docs for more info: https://svelte.dev/docs/kit/form-actions#named-actions" - ); - } -} -async function call_action(event, event_state, actions) { - const url = new URL(event.request.url); - let name = "default"; - for (const param of url.searchParams) { - if (param[0].startsWith("/")) { - name = param[0].slice(1); - if (name === "default") { - throw new Error('Cannot use reserved action name "default"'); - } - break; - } - } - const action = actions[name]; - if (!action) { - throw new SvelteKitError(404, "Not Found", `No action with name '${name}' found`); - } - if (!is_form_content_type(event.request)) { - throw new SvelteKitError( - 415, - "Unsupported Media Type", - `Form actions expect form-encoded data — received ${event.request.headers.get( - "content-type" - )}` - ); - } - return record_span({ - name: "sveltekit.form_action", - attributes: { - "http.route": event.route.id || "unknown" - }, - fn: async (current2) => { - const traced_event = merge_tracing(event, current2); - const result = await with_request_store( - { event: traced_event, state: event_state }, - () => action(traced_event) - ); - if (result instanceof ActionFailure) { - current2.setAttributes({ - "sveltekit.form_action.result.type": "failure", - "sveltekit.form_action.result.status": result.status - }); - } - return result; - } - }); -} -function validate_action_return(data) { - if (data instanceof Redirect) { - throw new Error("Cannot `return redirect(...)` — use `redirect(...)` instead"); - } - if (data instanceof HttpError) { - throw new Error("Cannot `return error(...)` — use `error(...)` or `return fail(...)` instead"); - } -} -function uneval_action_response(data, route_id, transport) { - const replacer = (thing) => { - for (const key2 in transport) { - const encoded = transport[key2].encode(thing); - if (encoded) { - return `app.decode('${key2}', ${uneval(encoded, replacer)})`; - } - } - }; - return try_serialize(data, (value) => uneval(value, replacer), route_id); -} -function stringify_action_response(data, route_id, transport) { - const encoders = Object.fromEntries( - Object.entries(transport).map(([key2, value]) => [key2, value.encode]) - ); - return try_serialize(data, (value) => stringify(value, encoders), route_id); -} -function try_serialize(data, fn, route_id) { - try { - return fn(data); - } catch (e) { - const error2 = ( - /** @type {any} */ - e - ); - if (data instanceof Response) { - throw new Error( - `Data returned from action inside ${route_id} is not serializable. Form actions need to return plain objects or fail(). E.g. return { success: true } or return fail(400, { message: "invalid" });`, - { cause: e } - ); - } - if ("path" in error2) { - let message = `Data returned from action inside ${route_id} is not serializable: ${error2.message}`; - if (error2.path !== "") message += ` (data.${error2.path})`; - throw new Error(message, { cause: e }); - } - throw error2; - } -} -function create_async_iterator() { - let resolved = -1; - let returned = -1; - const deferred = []; - return { - iterate: (transform = (x) => x) => { - return { - [Symbol.asyncIterator]() { - return { - next: async () => { - const next = deferred[++returned]; - if (!next) return { value: null, done: true }; - const value = await next.promise; - return { value: transform(value), done: false }; - } - }; - } - }; - }, - add: (promise) => { - deferred.push(with_resolvers()); - void promise.then((value) => { - deferred[++resolved].resolve(value); - }); - } - }; -} -function server_data_serializer(event, event_state, options2) { - let promise_id = 1; - let max_nodes = -1; - const iterator = create_async_iterator(); - const global = get_global_name(options2); - function get_replacer(index) { - return function replacer(thing) { - if (typeof thing?.then === "function") { - const id = promise_id++; - const promise = thing.then( - /** @param {any} data */ - (data) => ({ data }) - ).catch( - /** @param {any} error */ - async (error2) => ({ - error: await handle_error_and_jsonify(event, event_state, options2, error2) - }) - ).then( - /** - * @param {{data: any; error: any}} result - */ - async ({ data, error: error2 }) => { - let str; - try { - str = uneval(error2 ? [, error2] : [data], replacer); - } catch { - error2 = await handle_error_and_jsonify( - event, - event_state, - options2, - new Error(`Failed to serialize promise while rendering ${event.route.id}`) - ); - str = uneval([, error2], replacer); - } - return { - index, - str: `${global}.resolve(${id}, ${str.includes("app.decode") ? `(app) => ${str}` : `() => ${str}`})` - }; - } - ); - iterator.add(promise); - return `${global}.defer(${id})`; - } else { - for (const key2 in options2.hooks.transport) { - const encoded = options2.hooks.transport[key2].encode(thing); - if (encoded) { - return `app.decode('${key2}', ${uneval(encoded, replacer)})`; - } - } - } - }; - } - const strings = ( - /** @type {string[]} */ - [] - ); - return { - set_max_nodes(i) { - max_nodes = i; - }, - add_node(i, node) { - try { - if (!node) { - strings[i] = "null"; - return; - } - const payload = { type: "data", data: node.data, uses: serialize_uses(node) }; - if (node.slash) payload.slash = node.slash; - strings[i] = uneval(payload, get_replacer(i)); - } catch (e) { - e.path = e.path.slice(1); - throw new Error(clarify_devalue_error( - event, - /** @type {any} */ - e - ), { cause: e }); - } - }, - get_data(csp) { - const open = ``; - const close = `<\/script> -`; - return { - data: `[${compact(max_nodes > -1 ? strings.slice(0, max_nodes) : strings).join(",")}]`, - chunks: promise_id > 1 ? iterator.iterate(({ index, str }) => { - if (max_nodes > -1 && index >= max_nodes) { - return ""; - } - return open + str + close; - }) : null - }; - } - }; -} -function server_data_serializer_json(event, event_state, options2) { - let promise_id = 1; - const iterator = create_async_iterator(); - const reducers = { - ...Object.fromEntries( - Object.entries(options2.hooks.transport).map(([key2, value]) => [key2, value.encode]) - ), - /** @param {any} thing */ - Promise: (thing) => { - if (typeof thing?.then !== "function") { - return; - } - const id = promise_id++; - let key2 = "data"; - const promise = thing.catch( - /** @param {any} e */ - async (e) => { - key2 = "error"; - return handle_error_and_jsonify( - event, - event_state, - options2, - /** @type {any} */ - e - ); - } - ).then( - /** @param {any} value */ - async (value) => { - let str; - try { - str = stringify(value, reducers); - } catch { - const error2 = await handle_error_and_jsonify( - event, - event_state, - options2, - new Error(`Failed to serialize promise while rendering ${event.route.id}`) - ); - key2 = "error"; - str = stringify(error2, reducers); - } - return `{"type":"chunk","id":${id},"${key2}":${str}} -`; - } - ); - iterator.add(promise); - return id; - } - }; - const strings = ( - /** @type {string[]} */ - [] - ); - return { - add_node(i, node) { - try { - if (!node) { - strings[i] = "null"; - return; - } - if (node.type === "error" || node.type === "skip") { - strings[i] = JSON.stringify(node); - return; - } - strings[i] = `{"type":"data","data":${stringify(node.data, reducers)},"uses":${JSON.stringify( - serialize_uses(node) - )}${node.slash ? `,"slash":${JSON.stringify(node.slash)}` : ""}}`; - } catch (e) { - e.path = "data" + e.path; - throw new Error(clarify_devalue_error( - event, - /** @type {any} */ - e - ), { cause: e }); - } - }, - get_data() { - return { - data: `{"type":"data","nodes":[${strings.join(",")}]} -`, - chunks: promise_id > 1 ? iterator.iterate() : null - }; - } - }; -} -async function load_server_data({ event, event_state, state, node, parent }) { - if (!node?.server) return null; - let is_tracking = true; - const uses = { - dependencies: /* @__PURE__ */ new Set(), - params: /* @__PURE__ */ new Set(), - parent: false, - route: false, - url: false, - search_params: /* @__PURE__ */ new Set() - }; - const load = node.server.load; - const slash = node.server.trailingSlash; - if (!load) { - return { type: "data", data: null, uses, slash }; - } - const url = make_trackable( - event.url, - () => { - if (is_tracking) { - uses.url = true; - } - }, - (param) => { - if (is_tracking) { - uses.search_params.add(param); - } - } - ); - if (state.prerendering) { - disable_search(url); - } - const result = await record_span({ - name: "sveltekit.load", - attributes: { - "sveltekit.load.node_id": node.server_id || "unknown", - "sveltekit.load.node_type": get_node_type(node.server_id), - "http.route": event.route.id || "unknown" - }, - fn: async (current2) => { - const traced_event = merge_tracing(event, current2); - const result2 = await with_request_store( - { event: traced_event, state: event_state }, - () => load.call(null, { - ...traced_event, - fetch: (info, init2) => { - new URL(info instanceof Request ? info.url : info, event.url); - return event.fetch(info, init2); - }, - /** @param {string[]} deps */ - depends: (...deps) => { - for (const dep of deps) { - const { href } = new URL(dep, event.url); - uses.dependencies.add(href); - } - }, - params: new Proxy(event.params, { - get: (target, key2) => { - if (is_tracking) { - uses.params.add(key2); - } - return target[ - /** @type {string} */ - key2 - ]; - } - }), - parent: async () => { - if (is_tracking) { - uses.parent = true; - } - return parent(); - }, - route: new Proxy(event.route, { - get: (target, key2) => { - if (is_tracking) { - uses.route = true; - } - return target[ - /** @type {'id'} */ - key2 - ]; - } - }), - url, - untrack(fn) { - is_tracking = false; - try { - return fn(); - } finally { - is_tracking = true; - } - } - }) - ); - return result2; - } - }); - return { - type: "data", - data: result ?? null, - uses, - slash - }; -} -async function load_data({ - event, - event_state, - fetched, - node, - parent, - server_data_promise, - state, - resolve_opts, - csr -}) { - const server_data_node = await server_data_promise; - const load = node?.universal?.load; - if (!load) { - return server_data_node?.data ?? null; - } - const result = await record_span({ - name: "sveltekit.load", - attributes: { - "sveltekit.load.node_id": node.universal_id || "unknown", - "sveltekit.load.node_type": get_node_type(node.universal_id), - "http.route": event.route.id || "unknown" - }, - fn: async (current2) => { - const traced_event = merge_tracing(event, current2); - const child_state = { ...event_state, is_in_universal_load: true }; - return await with_request_store( - { event: traced_event, state: child_state }, - () => load.call(null, { - url: event.url, - params: event.params, - data: server_data_node?.data ?? null, - route: event.route, - fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts), - setHeaders: event.setHeaders, - depends: () => { - }, - parent, - untrack: (fn) => fn(), - tracing: traced_event.tracing - }) - ); - } - }); - return result ?? null; -} -function create_universal_fetch(event, state, fetched, csr, resolve_opts) { - const universal_fetch = async (input, init2) => { - const cloned_body = input instanceof Request && input.body ? input.clone().body : null; - const cloned_headers = input instanceof Request && [...input.headers].length ? new Headers(input.headers) : init2?.headers; - let response = await event.fetch(input, init2); - const url = new URL(input instanceof Request ? input.url : input, event.url); - const same_origin = url.origin === event.url.origin; - let dependency; - if (same_origin) { - if (state.prerendering) { - dependency = { response, body: null }; - state.prerendering.dependencies.set(url.pathname, dependency); - } - } else if (url.protocol === "https:" || url.protocol === "http:") { - const mode = input instanceof Request ? input.mode : init2?.mode ?? "cors"; - if (mode === "no-cors") { - response = new Response("", { - status: response.status, - statusText: response.statusText, - headers: response.headers - }); - } else { - const acao = response.headers.get("access-control-allow-origin"); - if (!acao || acao !== event.url.origin && acao !== "*") { - throw new Error( - `CORS error: ${acao ? "Incorrect" : "No"} 'Access-Control-Allow-Origin' header is present on the requested resource` - ); - } - } - } - let teed_body; - const proxy = new Proxy(response, { - get(response2, key2, receiver) { - async function push_fetched(body2, is_b64) { - const status_number = Number(response2.status); - if (isNaN(status_number)) { - throw new Error( - `response.status is not a number. value: "${response2.status}" type: ${typeof response2.status}` - ); - } - fetched.push({ - url: same_origin ? url.href.slice(event.url.origin.length) : url.href, - method: event.request.method, - request_body: ( - /** @type {string | ArrayBufferView | undefined} */ - input instanceof Request && cloned_body ? await stream_to_string(cloned_body) : init2?.body - ), - request_headers: cloned_headers, - response_body: body2, - response: response2, - is_b64 - }); - } - if (key2 === "body") { - if (response2.body === null) { - return null; - } - if (teed_body) { - return teed_body; - } - const [a, b] = response2.body.tee(); - void (async () => { - let result = new Uint8Array(); - for await (const chunk of a) { - const combined = new Uint8Array(result.length + chunk.length); - combined.set(result, 0); - combined.set(chunk, result.length); - result = combined; - } - if (dependency) { - dependency.body = new Uint8Array(result); - } - void push_fetched(base64_encode(result), true); - })(); - return teed_body = b; - } - if (key2 === "arrayBuffer") { - return async () => { - const buffer = await response2.arrayBuffer(); - const bytes = new Uint8Array(buffer); - if (dependency) { - dependency.body = bytes; - } - if (buffer instanceof ArrayBuffer) { - await push_fetched(base64_encode(bytes), true); - } - return buffer; - }; - } - async function text2() { - const body2 = await response2.text(); - if (body2 === "" && NULL_BODY_STATUS.includes(response2.status)) { - await push_fetched(void 0, false); - return void 0; - } - if (!body2 || typeof body2 === "string") { - await push_fetched(body2, false); - } - if (dependency) { - dependency.body = body2; - } - return body2; - } - if (key2 === "text") { - return text2; - } - if (key2 === "json") { - return async () => { - const body2 = await text2(); - return body2 ? JSON.parse(body2) : void 0; - }; - } - const value = Reflect.get(response2, key2, response2); - if (value instanceof Function) { - return Object.defineProperties( - /** - * @this {any} - */ - function() { - return Reflect.apply(value, this === receiver ? response2 : this, arguments); - }, - { - name: { value: value.name }, - length: { value: value.length } - } - ); - } - return value; - } - }); - if (csr) { - const get = response.headers.get; - response.headers.get = (key2) => { - const lower = key2.toLowerCase(); - const value = get.call(response.headers, lower); - if (value && !lower.startsWith("x-sveltekit-")) { - const included = resolve_opts.filterSerializedResponseHeaders(lower, value); - if (!included) { - throw new Error( - `Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://svelte.dev/docs/kit/hooks#Server-hooks-handle (at ${event.route.id})` - ); - } - } - return value; - }; - } - return proxy; - }; - return (input, init2) => { - const response = universal_fetch(input, init2); - response.catch(() => { - }); - return response; - }; -} -async function stream_to_string(stream) { - let result = ""; - const reader = stream.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) { - break; - } - result += text_decoder.decode(value); - } - return result; -} -function hash(...values) { - let hash2 = 5381; - for (const value of values) { - if (typeof value === "string") { - let i = value.length; - while (i) hash2 = hash2 * 33 ^ value.charCodeAt(--i); - } else if (ArrayBuffer.isView(value)) { - const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength); - let i = buffer.length; - while (i) hash2 = hash2 * 33 ^ buffer[--i]; - } else { - throw new TypeError("value must be a string or TypedArray"); - } - } - return (hash2 >>> 0).toString(36); -} -const replacements = { - "<": "\\u003C", - "\u2028": "\\u2028", - "\u2029": "\\u2029" -}; -const pattern = new RegExp(`[${Object.keys(replacements).join("")}]`, "g"); -function serialize_data(fetched, filter, prerendering = false) { - const headers2 = {}; - let cache_control = null; - let age = null; - let varyAny = false; - for (const [key2, value] of fetched.response.headers) { - if (filter(key2, value)) { - headers2[key2] = value; - } - if (key2 === "cache-control") cache_control = value; - else if (key2 === "age") age = value; - else if (key2 === "vary" && value.trim() === "*") varyAny = true; - } - const payload = { - status: fetched.response.status, - statusText: fetched.response.statusText, - headers: headers2, - body: fetched.response_body - }; - const safe_payload = JSON.stringify(payload).replace(pattern, (match) => replacements[match]); - const attrs = [ - 'type="application/json"', - "data-sveltekit-fetched", - `data-url="${escape_html(fetched.url, true)}"` - ]; - if (fetched.is_b64) { - attrs.push("data-b64"); - } - if (fetched.request_headers || fetched.request_body) { - const values = []; - if (fetched.request_headers) { - values.push([...new Headers(fetched.request_headers)].join(",")); - } - if (fetched.request_body) { - values.push(fetched.request_body); - } - attrs.push(`data-hash="${hash(...values)}"`); - } - if (!prerendering && fetched.method === "GET" && cache_control && !varyAny) { - const match = /s-maxage=(\d+)/g.exec(cache_control) ?? /max-age=(\d+)/g.exec(cache_control); - if (match) { - const ttl = +match[1] - +(age ?? "0"); - attrs.push(`data-ttl="${ttl}"`); - } - } - return ` - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-sveltekit/references/browser-sdk-2.md b/skills/integration/integration-sveltekit/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-sveltekit/references/browser-sdk-2.md +++ b/skills/integration/integration-sveltekit/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-sveltekit/references/browser-unified-sdk.md b/skills/integration/integration-sveltekit/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-sveltekit/references/browser-unified-sdk.md +++ b/skills/integration/integration-sveltekit/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-swift/SKILL.md b/skills/integration/integration-swift/SKILL.md index 9bca09a62..8c0e4fa6e 100644 --- a/skills/integration/integration-swift/SKILL.md +++ b/skills/integration/integration-swift/SKILL.md @@ -16,15 +16,15 @@ This skill helps you add Amplitude analytics to Swift (iOS/macOS) applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Swift (iOS/macOS) example project code -- `references/ios.md` - Amplitude documentation for Ios +- `references/ios.md` - Ios - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-swift/references/amplitude-quickstart.md b/skills/integration/integration-swift/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-swift/references/amplitude-quickstart.md +++ b/skills/integration/integration-swift/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-swift/references/ios.md b/skills/integration/integration-swift/references/ios.md index a2fb79b5a..eb38018ff 100644 --- a/skills/integration/integration-swift/references/ios.md +++ b/skills/integration/integration-swift/references/ios.md @@ -1,1480 +1,58 @@ - +[documentation](/docs) - - - - - - +[Get Started](/docs/get-started) - - - - - - - - - iOS | Amplitude - - - - - - - - - - - - - - - - - - +[Data](/docs/data) - - - +[AI Assistant](/docs/assistant) +[Experiment](/docs/experiment-home) - - -
-
-
-
- +[Admin](/docs/admin) - +[Partners](/docs/partners) -
-
+[FAQ](/docs/faq) -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+[SDKs](/docs/sdks) +/ -
-
+[Amplitude Analytics SDK Catalog](/docs/sdks/analytics) -
- -
- -
- - - - - - - - +/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +[iOS](/docs/sdks/analytics/ios) -
- -
-
-
-
- -
-
-
-

iOS

- -
-
- -
- current - -

- - - - - - iOS Swift SDK -

-
-
-
- -
- - - -
- maintenance - -

- - - - - - iOS SDK -

-
-
-
- -
- -
-
-
-
-
- -
- - +`current` [## ![](/docs/assets/icons/swift.svg) ![](/docs/assets/icons/obj-c.svg) iOS Swift SDK](/docs/sdks/analytics/ios/ios-swift-sdk) - - - - - - +[](https://cocoapods.org/pods/Amplitude-iOS) - - - - - - \ No newline at end of file +- [GitHub](https://github.com/amplitude/Amplitude-iOS) +- [Releases](https://github.com/amplitude/Amplitude-iOS/releases) \ No newline at end of file diff --git a/skills/integration/integration-tanstack-start/SKILL.md b/skills/integration/integration-tanstack-start/SKILL.md index 6e2083fbd..47ef0bd9f 100644 --- a/skills/integration/integration-tanstack-start/SKILL.md +++ b/skills/integration/integration-tanstack-start/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to TanStack Start applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - TanStack Start example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-tanstack-start/references/EXAMPLE.md b/skills/integration/integration-tanstack-start/references/EXAMPLE.md index 380e58662..0434dec99 100644 --- a/skills/integration/integration-tanstack-start/references/EXAMPLE.md +++ b/skills/integration/integration-tanstack-start/references/EXAMPLE.md @@ -785,170 +785,6 @@ function ProfilePage() { --- -## src/routeTree.gen.ts - -```ts -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -import { Route as rootRouteImport } from './routes/__root' -import { Route as ProfileRouteImport } from './routes/profile' -import { Route as BurritoRouteImport } from './routes/burrito' -import { Route as IndexRouteImport } from './routes/index' -import { Route as ApiBurritoConsiderRouteImport } from './routes/api/burrito/consider' -import { Route as ApiAuthLoginRouteImport } from './routes/api/auth/login' - -const ProfileRoute = ProfileRouteImport.update({ - id: '/profile', - path: '/profile', - getParentRoute: () => rootRouteImport, -} as any) -const BurritoRoute = BurritoRouteImport.update({ - id: '/burrito', - path: '/burrito', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const ApiBurritoConsiderRoute = ApiBurritoConsiderRouteImport.update({ - id: '/api/burrito/consider', - path: '/api/burrito/consider', - getParentRoute: () => rootRouteImport, -} as any) -const ApiAuthLoginRoute = ApiAuthLoginRouteImport.update({ - id: '/api/auth/login', - path: '/api/auth/login', - getParentRoute: () => rootRouteImport, -} as any) - -export interface FileRoutesByFullPath { - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute - '/api/auth/login': typeof ApiAuthLoginRoute - '/api/burrito/consider': typeof ApiBurritoConsiderRoute -} -export interface FileRoutesByTo { - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute - '/api/auth/login': typeof ApiAuthLoginRoute - '/api/burrito/consider': typeof ApiBurritoConsiderRoute -} -export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/burrito': typeof BurritoRoute - '/profile': typeof ProfileRoute - '/api/auth/login': typeof ApiAuthLoginRoute - '/api/burrito/consider': typeof ApiBurritoConsiderRoute -} -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/burrito' - | '/profile' - | '/api/auth/login' - | '/api/burrito/consider' - fileRoutesByTo: FileRoutesByTo - to: - | '/' - | '/burrito' - | '/profile' - | '/api/auth/login' - | '/api/burrito/consider' - id: - | '__root__' - | '/' - | '/burrito' - | '/profile' - | '/api/auth/login' - | '/api/burrito/consider' - fileRoutesById: FileRoutesById -} -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - BurritoRoute: typeof BurritoRoute - ProfileRoute: typeof ProfileRoute - ApiAuthLoginRoute: typeof ApiAuthLoginRoute - ApiBurritoConsiderRoute: typeof ApiBurritoConsiderRoute -} - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/profile': { - id: '/profile' - path: '/profile' - fullPath: '/profile' - preLoaderRoute: typeof ProfileRouteImport - parentRoute: typeof rootRouteImport - } - '/burrito': { - id: '/burrito' - path: '/burrito' - fullPath: '/burrito' - preLoaderRoute: typeof BurritoRouteImport - parentRoute: typeof rootRouteImport - } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/api/burrito/consider': { - id: '/api/burrito/consider' - path: '/api/burrito/consider' - fullPath: '/api/burrito/consider' - preLoaderRoute: typeof ApiBurritoConsiderRouteImport - parentRoute: typeof rootRouteImport - } - '/api/auth/login': { - id: '/api/auth/login' - path: '/api/auth/login' - fullPath: '/api/auth/login' - preLoaderRoute: typeof ApiAuthLoginRouteImport - parentRoute: typeof rootRouteImport - } - } -} - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - BurritoRoute: BurritoRoute, - ProfileRoute: ProfileRoute, - ApiAuthLoginRoute: ApiAuthLoginRoute, - ApiBurritoConsiderRoute: ApiBurritoConsiderRoute, -} -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() - -import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' -declare module '@tanstack/react-start' { - interface Register { - ssr: true - router: Awaited> - } -} - -``` - ---- - ## src/utils/amplitude-server.ts ```ts diff --git a/skills/integration/integration-tanstack-start/references/amplitude-quickstart.md b/skills/integration/integration-tanstack-start/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-tanstack-start/references/amplitude-quickstart.md +++ b/skills/integration/integration-tanstack-start/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-tanstack-start/references/browser-sdk-2.md b/skills/integration/integration-tanstack-start/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-tanstack-start/references/browser-sdk-2.md +++ b/skills/integration/integration-tanstack-start/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-tanstack-start/references/browser-unified-sdk.md b/skills/integration/integration-tanstack-start/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-tanstack-start/references/browser-unified-sdk.md +++ b/skills/integration/integration-tanstack-start/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/integration/integration-vue-3/SKILL.md b/skills/integration/integration-vue-3/SKILL.md index 6e9e67069..c2c8d595f 100644 --- a/skills/integration/integration-vue-3/SKILL.md +++ b/skills/integration/integration-vue-3/SKILL.md @@ -14,16 +14,16 @@ This skill helps you add Amplitude analytics to Vue 3 applications. Follow these steps in order to complete the integration: -1. `basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** -2. `basic-integration-1.1-edit.md` - Amplitude Setup - Edit -3. `basic-integration-1.2-revise.md` - Amplitude Setup - Revise -4. `basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion +1. `references/basic-integration-1.0-begin.md` - Amplitude Setup - Begin ← **Start here** +2. `references/basic-integration-1.1-edit.md` - Amplitude Setup - Edit +3. `references/basic-integration-1.2-revise.md` - Amplitude Setup - Revise +4. `references/basic-integration-1.3-conclude.md` - Amplitude Setup - Conclusion ## Reference files - `references/EXAMPLE.md` - Vue 3 example project code - `references/browser-unified-sdk.md` - Amplitude documentation for Browser Unified Sdk -- `references/browser-sdk-2.md` - Or install unified SDK to get access to all Amplitude products +- `references/browser-sdk-2.md` - Amplitude documentation for Browser Sdk 2 - `references/amplitude-quickstart.md` - Amplitude documentation for Amplitude Quickstart - `references/basic-integration-1.0-begin.md` - Amplitude setup - begin - `references/basic-integration-1.1-edit.md` - Amplitude setup - edit diff --git a/skills/integration/integration-vue-3/references/amplitude-quickstart.md b/skills/integration/integration-vue-3/references/amplitude-quickstart.md index c9780bea8..980bd220c 100644 --- a/skills/integration/integration-vue-3/references/amplitude-quickstart.md +++ b/skills/integration/integration-vue-3/references/amplitude-quickstart.md @@ -1,1845 +1,210 @@ - - - - - - - - - - - - - - - - - - Amplitude Quickstart | Amplitude - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+Three ways to get started. Click one to jump to the full setup guide. +[ -
-
+Setup wizard CLI -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Amplitude Quickstart

-
-
- - - - - - - - -

Get started with the full Amplitude platform in minutes. Which features drive retention? Where do users drop off? What's working and what's not? Traditional analytics implementations can take weeks or months. Amplitude's modern approach gets you from zero to insights in minutes, not months.

-

Use this kit to get Autocapture, Session Replay, Web Experiment, and Guides & Surveys.

-

Speed run your installation: Browser SDK and Autocapture

-

Quickly get data flowing into Amplitude with Browser SDK and Autocapture.

-

Install Amplitude

-

Need an API key? Create a free Amplitude account to get started.

-

When you create your Amplitude account, Amplitude provides two ways to install:

-
    -
  • A prompt which you can pass to your agentic AI tool of choice.
  • -
  • A pre-configured snippet, which you can add inside the <head> tag of every page you want to track.
  • -
-

-

Get your data flowing!

- -

Customize your installation by selecting the features and data region for your project.

- -
- -
- - -
- - - - - -
- Data region -
- - -
-
-
-
- - - - -
-
-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit or Bolt.

-

Click the Key icon to insert your Amplitude API key.

-
__AI_PROMPT_PLACEHOLDER__
-
-

Paste this snippet into the <head>of your site to track engagement.

-

Click the Key icon to insert your Amplitude API key.

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script><script>window.amplitude.add(window.sessionReplay.plugin({sampleRate: 1}));window.amplitude.init('AMPLITUDE_API_KEY', {"fetchRemoteConfig":true,"autocapture":{"attribution":true,"fileDownloads":true,"formInteractions":true,"pageViews":true,"sessions":true,"elementInteractions":true,"networkTracking":true,"webVitals":true,"frustrationInteractions":true}});</script>
-
-
- -
-

-

Use Autocapture

-

With autocapture: true, Amplitude automatically tracks:

-
    -
  • Sessions – How users engage over time.
  • -
  • Page views – Which pages matter most.
  • -
  • Clicks & interactions – What users engage with.
  • -
  • Form interactions – Where users get stuck.
  • -
  • File downloads – Content engagement.
  • -
  • Marketing attribution – Where users come from.
  • -
-

Take the next step

-

After your data starts flowing into Amplitude, expand your implementation with Session Replay, Web Experiment, and Guides & Surveys.

-

Add Session Replay

-

The snippet above includes Session Replay at 100% sampling (sampleRate: 1). This lets you watch exactly what users experience and helps you debug issues and understand user behavior. Lower the sample rate in production to control volume and cost.

-

Launch a Web Experiment

-

Web Experiment lets you run A/B tests on your website using the same data you already send through the Unified Browser SDK. Target users, roll out changes gradually, and compare variants based on real product metrics like activation or retention in Amplitude. Web Experiment handles experiment assignment, exposure, and basic statistical analysis so you don't need to build that logic yourself.

-

Test different versions or features of your site using Amplitude's no-code Visual Editor.

-

Create a guide or survey

-

Guides & Surveys let you deliver in-product messages and collect feedback on your website using the same Unified Browser SDK instrumentation you already set up. You can target users based on their behavior or properties, show walkthroughs or prompts, and trigger surveys at key moments in the journey. Amplitude captures responses and interactions, so you can see how they relate to core metrics like activation, conversion, or retention. This is a natural next step after events are flowing and you want to both influence and understand user behavior directly in the product.

-

Benefits of this approach

-
    -
  • Time to insight: Think minutes, not months. Start analyzing user behavior the same day.
  • -
  • No engineering bottleneck: Non-technical team members can define events using Visual Labeling.
  • -
  • Flexible foundation: Add custom precision tracking later as needs emerge.
  • -
  • Complete picture: Autocapture + Session Replay shows what users do and why.
  • -
  • Cost-effective: Only pay for what you use; scale as you grow.
  • -
- - -
-
- - +## Setup wizard CLI[](#setup-wizard-cli "Permalink") - +- Full SDK setup. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) +- [Guides & Surveys.](/docs/guides-and-surveys) +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. When you create your account, Amplitude generates a ready-to-use prompt with your API key. + +* * * + +## Browser snippet[](#browser-snippet "Permalink") + +Paste this script tag inside the `` of your site. You don't need a build step, dependencies, or codebase access. + +## Set up with a browser snippet + +Customize your installation by selecting the features and data region for your project. + + Autocapture Session Replay + +Data region + +US EU + +Click the Key icon to insert your Amplitude API key. + +```html + +``` + +**Where to run it:** Paste into your site's `` tag. + +**Time:** ~2 min. + +**What you get:** + +- Script tag. +- [Autocapture.](/docs/data/autocapture) +- [Session Replay.](/docs/session-replay) +- [Feature Experiment.](/docs/feature-experiment/workflow/feature-flag-rollouts) + +**Tips:** + +- Click the **Key** icon in the code block to insert your Amplitude API key. +- Need an API key? Create a [free Amplitude account](https://app.amplitude.com/signup) to get started. + +* * * + +## Take the next step[](#take-the-next-step "Permalink") + +Once data flows into Amplitude, expand your setup: + +- **Amplitude MCP server.** Query your analytics data from Claude, Cursor, or any MCP-compatible AI tool. [Learn about the Amplitude MCP server](/docs/amplitude-ai/amplitude-mcp). -
-
- -
-
- Was this page helpful? -
- -
-
- - -

December 11th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
+- **Claude Code plugin.** Install Amplitude as a [Claude Code plugin](https://github.com/amplitude/mcp-marketplace/tree/main/plugins/amplitude) for built-in slash commands. - - - - + Inside a Claude Code session: + + ```bash + /plugin install amplitude + ``` + + Or from your terminal: + + ```bash + claude plugin install amplitude + ``` + + The plugin includes slash commands like `/amplitude:create-chart`, `/amplitude:create-dashboard`, `/amplitude:instrument-events`, `/amplitude:replay-ux-audit`, and `/amplitude:weekly-brief`. + +- **Custom events.** Go beyond Autocapture and track events specific to your product. [Learn about tracking events](/docs/sdks/analytics/browser/browser-sdk-2#track-an-event). + +- **User identification.** Connect anonymous activity to known users. [Learn about identifying users](/docs/sdks/analytics/browser/browser-sdk-2#set-a-user-id). - - - - - - - - - - - - \ No newline at end of file +- **Group analytics.** Analyze behavior at the account or team level. [Learn about user groups](/docs/sdks/analytics/browser/browser-sdk-2#user-groups). \ No newline at end of file diff --git a/skills/integration/integration-vue-3/references/browser-sdk-2.md b/skills/integration/integration-vue-3/references/browser-sdk-2.md index 3d8d33798..287c263ab 100644 --- a/skills/integration/integration-vue-3/references/browser-sdk-2.md +++ b/skills/integration/integration-vue-3/references/browser-sdk-2.md @@ -1,1244 +1,33 @@ - +Amplitude's Browser SDK 2 lets you send events to Amplitude. - - - - - - +## Tip - - - - - - - - - Browser SDK 2 | Amplitude - - - - - - - - - - - - - - - - - - - - - - +Install the [Browser Unified SDK](/docs/sdks/analytics/browser/browser-unified-sdk) to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components. +## Initialize the SDK[](#initialize-the-sdk "Permalink") - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
+## Load and initialize only when context is ready +Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL). -
-
+## Sending events -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser SDK 2

-
-
- - - - - - - - -

Amplitude's Browser SDK 2 lets you send events to Amplitude.

-

Install the SDK

-

Install the dependency with npm, yarn, or the script loader.

-

-

Unified SDK

-Install the Browser Unified SDK to access the Experiment SDK along with other Amplitude products (Analytics, Session Replay). The Unified SDK provides a single entry point for all Amplitude features and simplifies the integration process by handling the initialization and configuration of all components.
-

-

-

-
-
- -
-When you use the script loader and enable Autocapture, Browser SDK track interactions on your site automatically. For more information, see Autocapture. -
-
-
-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
<script src="https://cdn.eu.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
-  window.amplitude.init('AMPLITUDE_API_KEY', {
-    serverZone: 'EU',
-    fetchRemoteConfig: true,
-    autocapture: true
-  });
-</script>
-
-
-
- -
-

-
# Install Analytics SDK only
-npm install @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-npm install @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-
# Install Analytics SDK only
-yarn add @amplitude/analytics-browser
-# Or install Unified SDK to get access to all Amplitude products
-yarn add @amplitude/unified
-
-

Import Amplitude into your project

-
// If using Analytics SDK only
-import * as amplitude from '@amplitude/analytics-browser';
-// If using Unified SDK
-import * as amplitude from '@amplitude/unified';
-
-

-

-

-

Initialize the SDK

-

-

Load and initialize only when context is ready

-Don't load the Amplitude SDK from third-party scripts that run before the page has fully loaded. In those setups, user identifiers, traits, and page URL or state often aren't available yet, so early events can be sent with missing or incorrect properties. Initialize the SDK only after your app has access to all relevant data (for example, user ID, user properties, and the final page URL).
-

-

-

Sending events

-This SDK uses the HTTP V2 API and follows the same constraints for events. Make sure that all events logged in the SDK have the event_type field and at least one of deviceId  (included by default) or userId, and follow the HTTP API's constraints on each of those fields.

-

To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a userId or deviceId value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the minIdLength config option.

-

-

This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional userID and config object in this call.

-
// Option 1, initialize with Amplitude API key only
+This SDK uses the [HTTP V2](/docs/apis/analytics/http-v2) API and follows the same constraints for events. Make sure that all events logged in the SDK have the `event_type` field and at least one of `deviceId`  (included by default) or `userId`, and follow the HTTP API's constraints on each of those fields.
+
+To prevent instrumentation issues, device IDs and user IDs must be strings with a length of 5 characters or more. If an event contains a device ID or user ID that's too short, the ID value is removed from the event. If the event doesn't have a `userId` or `deviceId` value, Amplitude may reject the upload with a 400 status. Override the default minimum length of 5 characters by setting the `minIdLength` config option.
+
+This SDK requires initialization before you can instrument any events and requires your Amplitude project's API key. You can pass an optional `userID` and `config` object in this call.
+
+```js
+// Option 1, initialize with Amplitude API key only
 amplitude.init(AMPLITUDE_API_KEY);
 
 // Option 2, initialize with options
@@ -1249,567 +38,221 @@ amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com');
 
 // Option 4, initialize with a user ID and options
 amplitude.init(AMPLITUDE_API_KEY, 'user@amplitude.com', options);
-
-

-

Warning

-When using the SDK in an Angular app with Zone.js, invoke init outside of the Angular zone.

-
runOutsideAngular(function () { amplitude.init(...args); })
-
-

The Angular zone overwrites certain DOM functions that, when invoked by Amplitude autocapture, causes some user interactions to break

-

-

-

Next.js Integration

-For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the Next.js Installation Guide.
-

-

Configure the SDK

-

- SDK configuration options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
instanceNamestring. The instance name.$default_instance
flushIntervalMillisnumber. Sets the interval of uploading events to Amplitude in milliseconds.1,000 (1 second)
flushQueueSizenumber. Sets the maximum number of events batched in a single upload attempt.30 events
flushMaxRetriesnumber. Sets the maximum number of retries for failed upload attempts. This is only applicable to errors that the SDK can retry.5 times.
logLevelLogLevel.None or LogLevel.Error or LogLevel.Warn or LogLevel.Verbose or LogLevel.Debug. Sets the log level. You can also use numeric values: 0 (None), 1 (Error), 2 (Warn), 3 (Verbose), or 4 (Debug).LogLevel.Warn
loggerProvider Logger. Sets a custom loggerProvider class that implements the Logger interface to emit log messages to a specified destination.Amplitude Logger
minIdLengthnumber. Sets the minimum length for the value of userId and deviceId properties.5
optOutboolean. Sets permission to track events. Setting a value of true prevents Amplitude from tracking and uploading events.false
serverUrlstring. Sets the URL where events are upload to.https://api2.amplitude.com/2/httpapi
serverZoneEU or US. Sets the Amplitude server zone. Set this to EU for Amplitude projects created in EU data center.US
useBatchboolean. Sets whether to upload events to Batch API instead of the default HTTP V2 API or not.false
appVersionstring. Sets an app version for events tracked. This can be the version of your application. For example: "1.0.0"undefined
autocaptureboolean|AutocaptureOptions. Configures autocapture tracking. See Autocapture.
defaultTrackingboolean. Deprecated in version 2.10.0. Use autocapture instead. Configures default event tracking.true
deviceIdstring. Sets an identifier for the device running your application.UUID()
identifyIdentify. Calls "identify" with this object during initialization. Called before Autocapture events, like session_start, ensuring proper attribution of events.undefined
cookieOptions.domainstring. Sets the domain property of cookies created.undefined
cookieOptions.expirationnumber. Sets expiration of cookies created in days.365 days
cookieOptions.sameSitestring. Sets SameSite property of cookies created.Lax
cookieOptions.secureboolean. Sets Secure property of cookies created.false
cookieOptions.upgradeboolean. Sets upgrading from cookies created by maintenance Browser SDK. If true, new Browser SDK deletes cookies created by maintenance Browser SDK. If false, Browser SDK keeps cookies created by maintenance Browser SDK.true
identityStoragestring. Sets storage API for user identity. Options include cookie for document.cookie, localStorage for localStorage, sessionStorage for sessionStorage, or none to opt-out of persisting user identity.cookie
partnerIdstring. Sets partner ID. Amplitude requires the customer who built an event ingestion integration to add the partner identifier to partner_id.undefined
sessionTimeoutnumber. Sets the period of inactivity from the last tracked event before a session expires in milliseconds.1,800,000 milliseconds (30 minutes)
storageProviderStorage<Event[]>. Sets a custom implementation of Storage<Event[]> to persist unsent events.LocalStorage
userIdstring. Sets an identifier for the tracked user. Must have a minimum length of 5 characters unless overridden with the minIdLength option.undefined
trackingOptionsTrackingOptions. Configures tracking of extra properties.Enable all tracking options by default.
transportTransportType | TransportConfig. Sets the request API to use. Pass a string ('fetch', 'xhr', or 'beacon'), or an object with type and headers properties to set custom HTTP headers. Go to Custom HTTP request headers for more details.'fetch'
offlineboolean. Whether the SDK connects to the network. See Offline modefalse
fetchRemoteConfigboolean. Deprecated. Use remoteConfig.fetchRemoteConfig instead. Whether the SDK fetches remote configuration. See Remote configurationstrue
remoteConfigobject. Remote configuration options. Go to Remote configurationfetchRemoteConfig - boolean. Whether the SDK fetches remote configuration. Default: trueserverUrl - string. Custom server URL for proxying remote config requestsundefined
-

-

-

-

Configure batching behavior

-

To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the track method logs. Customize this behavior with the flushQueueSize and flushIntervalMillis configuration parameters. If you plan to send large batches of data at once, set useBatch to true and setServerUrl to the batch API: https://api2.amplitude.com/batch. Both standard and batch modes use the same event upload threshold and flush time intervals

-

EU data residency

-

To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.

-
amplitude.init(AMPLITUDE_API_KEY, {
+```
+
+## Warning
+
+When using the SDK in an [Angular](https://angular.dev/) app with [Zone.js](https://angular.dev/api/core/NgZone), invoke `init` [outside of the Angular zone](https://angular.dev/api/core/NgZone#runOutsideAngular).
+
+```javascript
+runOutsideAngular(function () { amplitude.init(...args); })
+```
+
+The Angular zone overwrites certain DOM functions that, when invoked by [Amplitude autocapture](/docs/sdks/analytics/browser/browser-sdk-2#autocapture), causes some user interactions to break
+
+## Next.js Integration
+
+For detailed instructions on integrating Amplitude with Next.js applications, including both client-side and server-side setups, see the [Next.js Installation Guide](/docs/sdks/frameworks/nextjs-installation-guide).
+
+## Configure the SDK[](#configure-the-sdk "Permalink")
+
+### Configure batching behavior[](#configure-batching-behavior "Permalink")
+
+To support high-performance environments, the SDK sends events in batches. The SDK queues in memory every event the `track` method logs. Customize this behavior with the `flushQueueSize` and `flushIntervalMillis` configuration parameters. If you plan to send large batches of data at once, set `useBatch` to `true` and `setServerUrl` to the batch API: `https://api2.amplitude.com/batch`. Both standard and batch modes use the same event upload threshold and flush time intervals
+
+### EU data residency[](#eu-data-residency "Permalink")
+
+To send data to Amplitude's EU-based servers, set the server zone when you initialize the client. If set, the SDK sends to the region determined by this setting.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   serverZone: 'EU',
 });
-
-

-

Data residency requirement

-To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
-

-

Debugging

-

Control the level of logs the SDK prints to the console with the following logLevel settings:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log levelDescription
noneSuppresses all log messages
errorShows error messages only
warnDefault. Shows error and warning messages.
verboseShows informative messages.
debugShows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
-

Set the logLevel parameter.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+## Data residency requirement
+
+To send data to Amplitude's EU servers, your organization must use the EU data storage region, which you set during signup.
+
+### Debugging[](#debugging "Permalink")
+
+Control the level of logs the SDK prints to the console with the following `logLevel` settings:
+
+Log level
+
+Description
+
+`none`
+
+Suppresses all log messages
+
+`error`
+
+Shows error messages only
+
+`warn`
+
+Default. Shows error and warning messages.
+
+`verbose`
+
+Shows informative messages.
+
+`debug`
+
+Shows all messages, including function context information for each public method the SDK invokes. Amplitude recommends this log level for development only.
+
+Set the `logLevel` parameter.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Warn,
 });
-
-

-

Note

-In environments where you can't import the LogLevel enum (such as Google Tag Manager), use numeric values instead:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Numeric valueLog levelEnum equivalent
0NoneLogLevel.None
1ErrorLogLevel.Error
2WarnLogLevel.Warn
3VerboseLogLevel.Verbose
4DebugLogLevel.Debug
-

For example, to suppress all logs in GTM, set logLevel to 0:

-
// In GTM configuration
+```
+
+## Note
+
+In environments where you can't import the `LogLevel` enum (such as Google Tag Manager), use numeric values instead:
+
+Numeric value
+
+Log level
+
+Enum equivalent
+
+`0`
+
+None
+
+`LogLevel.None`
+
+`1`
+
+Error
+
+`LogLevel.Error`
+
+`2`
+
+Warn
+
+`LogLevel.Warn`
+
+`3`
+
+Verbose
+
+`LogLevel.Verbose`
+
+`4`
+
+Debug
+
+`LogLevel.Debug`
+
+For example, to suppress all logs in GTM, set `logLevel` to `0`:
+
+```js
+// In GTM configuration
 logLevel: 0
-
-

Don't use string values like "LogLevel.None" in GTM, as these won't work correctly.

-

-

The default logger outputs log to the developer console. You can provide your own logger implementation based on the Logger interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.

-

Set the logger by configuring the loggerProvider with your own implementation.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+Don't use string values like `"LogLevel.None"` in GTM, as these won't work correctly.
+
+The default logger outputs log to the developer console. You can provide your own logger implementation based on the `Logger` interface for any customization purpose. For example, collecting any error messages from the SDK in a production environment.
+
+Set the logger by configuring the `loggerProvider` with your own implementation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   loggerProvider: new MyLogger(),
 });
-
-

Debug mode

-

Enable the debug mode by setting the logLevel to "Debug", for example:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Debug mode[](#debug-mode "Permalink")
+
+Enable the debug mode by setting the `logLevel` to "Debug", for example:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   logLevel: amplitude.Types.LogLevel.Debug,
 });
-
-

With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:

-
    -
  • type: Category of this context, for example "invoke public method".
  • -
  • name: Name of invoked function, for example "track".
  • -
  • args: Arguments of the invoked function.
  • -
  • stacktrace: Stacktrace of the invoked function.
  • -
  • time: Start and end timestamp of the function invocation.
  • -
  • states: Useful internal states snapshot before and after the function invocation.
  • -
-

Performance

-

The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.

-

Bundle size

-

The Browser SDK 2 bundle size varies based on the installation method and features you use.

-

+``` - -

-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/analytics-browser -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - - - - -

-

For the most up-to-date bundle size information, check the npm package page or BundlePhobia.

-

Runtime performance

-

The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:

-
    -
  • Event tracking: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
  • -
  • Network requests: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
  • -
  • Memory usage: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
  • -
  • CPU impact: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
  • -
-

Optimization tips

-

To further optimize performance:

-
    -
  • Adjust flushQueueSize and flushIntervalMillis to balance between network efficiency and memory usage.
  • -
  • Use the offline mode to defer event uploads when network conditions are poor.
  • -
  • Enable useBatch mode for high-volume event tracking to reduce the number of HTTP requests.
  • -
-

Autocapture

-

Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:

-
    -
  • Attribution
  • -
  • Page views
  • -
  • Sessions
  • -
  • Form interactions
  • -
  • File downloads
  • -
  • Element interactions
  • -
  • Page URL enrichment
  • -
  • Network tracking
  • -
  • Web vitals
  • -
-

- Autocapture options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attributionOptional. Type: boolean. Enables/disables marketing attribution tracking. If true, Amplitude tracks marketing attribution events. Default value is true.
config.autocapture.pageViewsOptional. Type: boolean. Enables/disables default page view tracking. If true, Amplitude tracks page view events on initialization. Event properties tracked includes: [Amplitude] Page Domain, [Amplitude] Page Location, [Amplitude] Page Path, [Amplitude] Page Title, [Amplitude] Page URL. Default value is true. See Track page views for more information.
config.autocapture.sessionsOptional. Type: boolean. Enables/disables session tracking. If true, Amplitude tracks session start and session end events otherwise, Amplitude doesn't track session events. When this setting is false, Amplitude tracks sessionId only. Default value is true. See Track sessions for more information.
config.autocapture.formInteractionsOptional. Type: boolean. Enables/disables form interaction tracking. If true, Amplitude tracks form start and form submit events. Event properties tracked includes: [Amplitude] Form ID, [Amplitude] Form Name, [Amplitude] Form Destination. Default value is true. See Track form interactions for more information.
config.autocapture.fileDownloadsOptional. Type: boolean. Enables/disables file download tracking. If true, Amplitude tracks file download events otherwise. Event properties tracked includes: [Amplitude] File Extension, [Amplitude] File Name, [Amplitude] Link ID, [Amplitude] Link Text, [Amplitude] Link URL. Default value is true. See Track file downloads for more information.
config.autocapture.elementInteractionsOptional. Type: boolean. Enables/disables element interaction tracking. If true, Amplitude tracks clicks and form field interactions. Default value is false. Go to Track element interactions for more information and configuration options.
config.autocapture.frustrationInteractionsOptional. Type: boolean. Enables/disables frustration interaction tracking. If true, Amplitude tracks rage clicks and dead clicks. Default value is false. Review Track frustration interactions for more information and configuration options. Minimum SDK version 2.24.0
config.autocapture.pageUrlEnrichmentOptional. Type: boolean. Enables/disables page URL enrichment tracking. If true, Amplitude automatically adds page URL-related properties to all events, including previous page information and page type classification. Default value is true. Go to Page URL enrichment plugin for more information.
config.autocapture.networkTrackingOptional. Type: boolean. Enables/disables capturing network request events invoked by XHR and Fetch. If true, Amplitude tracks failed network requests. To configure what gets captured, set this as a network tracking options object. Default value is false. See Track network interactions for more information and configuration options.
config.autocapture.webVitalsOptional. Type: boolean. Enables/disables Core Web Vitals tracking. If true, Amplitude automatically captures web performance metrics (INP, LCP, FCP, CLS, TTFB) and sends them as [Amplitude] Web Vitals events. Default value is false. See Track web vitals for more information. Minimum SDK version 2.27.0.
-

-

-

-

Remote configuration

-

Autocapture supports remote configuration. For more information, see Autocapture Settings.

-

Disable Autocapture

-

To disable Autocapture, see the following code sample.

-
// Disable individual default tracked events
+With the default logger, extra function context information is output to the developer console when invoking any SDK public method, including:
+
+-   `type`: Category of this context, for example "invoke public method".
+-   `name`: Name of invoked function, for example "track".
+-   `args`: Arguments of the invoked function.
+-   `stacktrace`: Stacktrace of the invoked function.
+-   `time`: Start and end timestamp of the function invocation.
+-   `states`: Useful internal states snapshot before and after the function invocation.
+
+## Performance[](#performance "Permalink")
+
+The Browser SDK 2 minimizes its impact on page performance through event batching, asynchronous processing, and optimizing bundle sizes.
+
+### Bundle size[](#bundle-size "Permalink")
+
+The Browser SDK 2 bundle size varies based on the installation method and features you use.
+
+### [Package Information](https://npmjs.com/package/@amplitude/analytics-browser) Live
+
+Package Name
+
+`@amplitude/analytics-browser`
+
+Version
+
+2.42.0
+
+Size (gzip)
+
+57.15 kB
+
+For the most up-to-date bundle size information, check the [npm package page](https://www.npmjs.com/package/@amplitude/analytics-browser) or [BundlePhobia](https://bundlephobia.com/package/@amplitude/analytics-browser).
+
+### Runtime performance[](#runtime-performance "Permalink")
+
+The Browser SDK 2 runs asynchronously and doesn't block the main thread during event tracking. Performance characteristics include:
+
+-   **Event tracking**: Event tracking operations are non-blocking and typically complete in less than 1ms for each event.
+-   **Network requests**: Events are batched and sent asynchronously, minimizing network overhead. The default configuration batches up to 30 events or sends every 1 second, whichever comes first.
+-   **Memory usage**: The SDK maintains a small in-memory queue for event batching. Memory usage scales with the number of queued events (default: up to 30 events).
+-   **CPU impact**: Event processing and batching operations have minimal CPU impact, typically less than 1% of CPU time during normal operation.
+
+### Optimization tips[](#optimization-tips "Permalink")
+
+To further optimize performance:
+
+-   Adjust `flushQueueSize` and `flushIntervalMillis` to balance between network efficiency and memory usage.
+-   Use the `offline` mode to defer event uploads when network conditions are poor.
+-   Enable `useBatch` mode for high-volume event tracking to reduce the number of HTTP requests.
+
+## Autocapture[](#autocapture "Permalink")
+
+Starting in SDK version 2.10.0, the Browser SDK can autocapture events when you enable it, and adds a configuration to control the collection of autocaptured events. Browser SDK can autocapture the following event types:
+
+-   Attribution
+-   Page views
+-   Sessions
+-   Form interactions
+-   File downloads
+-   Element interactions
+-   Page URL enrichment
+-   Network tracking
+-   Web vitals
+
+### Remote configuration[](#remote-configuration "Permalink")
+
+Autocapture supports [remote configuration](#remote-configuration). For more information, see [Autocapture Settings](/docs/data/amplitude-data-settings#autocapture).
+
+### Disable Autocapture[](#disable-autocapture "Permalink")
+
+To disable Autocapture, see the following code sample.
+
+```ts
+// Disable individual default tracked events
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false,
@@ -1827,1180 +270,618 @@ amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
 amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: false,
 });
-
-

Track marketing attribution

-

Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs as user properties.

-

- Attribution overview - -
-Amplitude tracks marketing attribution to identify your user's traffic source using the UTM, referrer and click ID parameters.

-

UTM parameters

-

UTM (Urchin Traffic Monitor) parameters are useful for analyzing the effectiveness of different ad campaigns and referring sites. UTM parameters are case-sensitive, so they're treated as different values when the capitalization varies.

-

There are five different standard UTM parameters:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_termThis identifies paid search terms used (for example, product+analytics)
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
-

Here is an example URL with UTM parameters:

-
https://www.amplitude.com/?utm_source=newsletter&utm_campaign=product_analytics_playbook&utm_medium=email&utm_term=product%20analytics&utm_content=banner-link
-
-

Referrer parameters

-

Referrer is the URL of the page that linked to the destination page. Amplitude tracks the following parameters:

- - - - - - - - - - - - - - - - - -
NameDescription
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
-

Referrer is an empty string ('') if the user navigated to the destination page directly.

-

Click ID parameters

-

Click IDs are campaign identifiers included as URL query parameters. Ad platforms use these IDs to identify the campaign and other attributes. While Amplitude doesn't have access to further campaign attributes associated to Click IDs, Amplitude can track Click ID values specified in the following table.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
dclidGoogle Marketing Platform click identifier
fbclidFacebook click identifier
gbraidGoogle click identifier on iOS for web-to-app measurement
wbraidGoogle click identifier on iOS for app-to-web measurement
gclidGoogle click identifier
ko_click_idKochava click identifier
li_fat_idLinkedIn click identifier
msclkidMicrosoft click identifier
rdt_cidReddit click identifier
ttclidTikTok click identifier
twclidTwitter click identifier
-

First-touch attribution

-

Amplitude captures the initial attribution data at the start of the first session. The first-touch attribution values are set when Amplitude sees a user's attribution data for the first time. The following user properties are set one time:

-
    -
  • initial_utm_source
  • -
  • initial_utm_medium
  • -
  • initial_utm_campaign
  • -
  • initial_utm_term
  • -
  • initial_utm_content
  • -
  • initial_referrer
  • -
  • initial_referring_domain
  • -
  • initial_gclid
  • -
  • initial_fbclid
  • -
  • initial_dclid
  • -
  • initial_gbraid
  • -
  • initial_ko_click_id
  • -
  • initial_msclkid
  • -
  • initial_ttclid
  • -
  • initial_twclid
  • -
  • initial_wbraid
  • -
  • initial_li_fat_id
  • -
  • initial_rdt_cid
  • -
-

Multi-touch attribution

-

Amplitude captures the attribution data at the start of each session, and sets those values as user properties. For organic or direct traffic, these properties may not be available. Therefore, these user properties are unset from user identity.

-

For every new campaign, Amplitude captures the changes regardless of the state of the user session. You can configure resetSessionOnNewCampaign to true to reset the session on every new campaign. The default behavior is to not reset the session on new campaign.

-

Amplitude tracks the following as user properties:

-
    -
  • utm_source
  • -
  • utm_medium
  • -
  • utm_campaign
  • -
  • utm_term
  • -
  • utm_content
  • -
  • referrer
  • -
  • referring_domain
  • -
  • gclid
  • -
  • fbclid
  • -
  • dclid
  • -
  • gbraid
  • -
  • ko_click_id
  • -
  • msclkid
  • -
  • ttclid
  • -
  • twclid
  • -
  • wbraid
  • -
  • li_fat_id
  • -
  • rdt_cid -
-
- -

Set config.autocapture.attribution to false to disable marketing attribution tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+### Track marketing attribution[](#track-marketing-attribution "Permalink")
+
+Amplitude tracks marketing attribution by default. Browser SDK 2 captures UTM parameters, referrer information, and click IDs.
+
+You can choose how the SDK persists campaign attribution data:
+
+-   **User property tracking** (default): Tracks campaign parameters as user properties through identify events for first-touch and multi-touch attribution.
+-   **Event property tracking**: Attaches campaign parameters to each event's properties, giving you event-level attribution granularity. Use with [Persisted Properties](/docs/data/persisted-properties) to select different attribution models.
+
+Set `config.autocapture.attribution` to `false` to disable marketing attribution tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: false, 
   },
 });
-
-

Advanced configuration for marketing attribution tracking

-

- Marketing attribution configuration - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.attribution.excludeReferrersOptional. Type: Array of string or RegExp. Sets rules to decide which referrers to exclude from tracking as traffic source. Use string values for exact matching and RegExp values for pattern matching against the referring domain. When this option isn't set, the SDK excludes the current domain (and its subdomains). If explicitly adding an external referrer to exclude, you must also add the current domain (and its subdomains) as more referrers to exclude.
config.autocapture.attribution.excludeInternalReferrersOptional. Type: boolean or { condition: 'always' | 'ifEmptyCampaign' }. When enabled, the SDK does not track campaign information when the referrer and the current page are on the same domain (internal referrer). Set to true or { condition: 'always' } to always skip campaign tracking for internal referrers. Set to { condition: 'ifEmptyCampaign' } to always skip campaign tracking for internal referrers where there are no UTM parameters or click IDs (empty campaign).
config.autocapture.attribution.initialEmptyValueOptional. Type: string. Sets the value to represent undefined/no initial campaign parameter for first-touch attribution. The default value is "EMPTY".
config.autocapture.attribution.resetSessionOnNewCampaignOptional. Type: boolean. Configures Amplitude to start a new session if any campaign parameter changes. The default value is false.
-

-

-

-
Exclude internal referrers
-

Use excludeInternalReferrers when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when document.referrer and location.hostname resolve to the same domain.

-
    -
  • Always exclude: Set excludeInternalReferrers: true or excludeInternalReferrers: { condition: 'always' } to never track campaign information for internal referrers.
  • -
  • Exclude only when campaign is empty: Set excludeInternalReferrers: { condition: 'ifEmptyCampaign' } to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by excludeReferrers)
  • -
-

- Example: always exclude internal referrers - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    attribution: {
-      excludeInternalReferrers: true,
-    },
-  },
-});
-
-

-

-

-

- Example: exclude internal referrers only when campaign is empty - -

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for marketing attribution tracking[](#advanced-configuration-for-marketing-attribution-tracking "Permalink")
+
+#### Event property tracking[](#event-property-tracking "Permalink")
+
+## Version requirement
+
+Event property attribution tracking requires Browser SDK version 2.40.0 or later.
+
+Configure the SDK to attach campaign parameters to every event's properties instead of (or in addition to) user properties. This gives you event-level attribution granularity.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeInternalReferrers: { condition: 'ifEmptyCampaign' },
+      trackingMethod: 'eventProperty',
     },
   },
 });
-
-

-

-

-
Exclude referrers
-

-

Note

-All sub-configurations of config.autocapture.attribution take effect only on user properties and do NOT affect the event properties of the default page view events.
-

-

The default value of config.autocapture.attribution.excludeReferrers is the top level domain with cookie storage enabled. For example, if you initialize the SDK on https://www.docs.developers.amplitude.com/, the SDK first checks amplitude.com. If it doesn't allow cookie storage, then the SDK checks developers.amplitude.com and subsequent subdomains. If it allows cookie storage, then the SDK sets excludeReferrers to an RegExp object /amplitude\.com$/ which matches and then exlucdes tracking referrers from all subdomains of amplitude.com, for example, data.amplitude.com, analytics.amplitude.com and etc.

-

In addition to excluding referrers from the default configuration, you can add other domains by setting the custom excludeReferrers. Custom excludeReferrers overrides the default values. For example, to also exclude referrers from google.com, set excludeReferrers to [/amplitude\.com$/, 'google.com'].

-

- Example of including all referrers - -
-Track complete web attribution, including self-referrals, for comprehensive insight.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+With event property tracking, the SDK:
+
+-   Parses campaign parameters on page load and SPA navigations (History API changes like `pushState`, `replaceState`, `popstate`).
+-   Attaches campaign fields to the `event_properties` of every tracked event.
+
+## Note
+
+Event property tracking doesn't set user properties. If you need both event-level attribution and user properties, enable both methods:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      // Override the default setting to exclude all subdomains
-      excludeReferrers: [],
-    },
-  },
-});
-
-

-

-

-

- Example of excluding all self-referrals and other subdomains - -
-For customers who want to exclude tracking campaign from any referrers across all subdomains of your-domain.com, as well as from a specific subdomain.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-    autocapture: {
-    attribution: {
-      excludeReferrers: [/your-domain\.com$/, 'www.test.com'],
+      trackingMethod: ['userProperty', 'eventProperty'],
     },
   },
 });
-
-

-

-

-

- Exclude referrers that match a specific pattern - -
-For customers who want to exclude tracking campaign from all referrers across all subdomains of test.com.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Fallback attribution event[](#fallback-attribution-event "Permalink")
+
+When using event property tracking, enable `fallbackAttributionEvent` to ensure campaign data is captured even when users don't trigger other events. This fires an `[Amplitude] Attribution` event on each page view and SPA navigation.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     attribution: {
-      excludeReferrers: [/test\.com$/],
+      trackingMethod: 'eventProperty',
+      fallbackAttributionEvent: true,
     },
   },
 });
-
-

-

-

-

Track page views

-

Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is [Amplitude] Page Viewed.

-

Set config.autocapture.pageViews to false to disable page view tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+##### Exclude internal referrers[](#exclude-internal-referrers "Permalink")
+
+Use `excludeInternalReferrers` when you want to avoid attributing traffic to internal navigation (same domain or subdomain). The SDK treats a referrer as internal when `document.referrer` and `location.hostname` resolve to the same domain.
+
+-   **Always exclude:** Set `excludeInternalReferrers: true` or `excludeInternalReferrers: { condition: 'always' }` to never track campaign information for internal referrers.
+-   **Exclude only when campaign is empty:** Set `excludeInternalReferrers: { condition: 'ifEmptyCampaign' }` to skip campaign tracking for internal referrers where there are no UTM parameters or click IDs. If the user arrives from an internal page with UTM or click IDs, campaign data is still tracked (if the referrer isn't excluded by `excludeReferrers`)
+
+##### Exclude referrers[](#exclude-referrers "Permalink")
+
+## Note
+
+All sub-configurations of `config.autocapture.attribution` take effect only on user properties and do **NOT** affect the event properties of the default page view events.
+
+The default value of `config.autocapture.attribution.excludeReferrers` is the top level domain with cookie storage enabled. For example, if you initialize the SDK on `https://www.docs.developers.amplitude.com/`, the SDK first checks `amplitude.com`. If it doesn't allow cookie storage, then the SDK checks `developers.amplitude.com` and subsequent subdomains. If it allows cookie storage, then the SDK sets `excludeReferrers` to an RegExp object `/amplitude\.com$/` which matches and then exlucdes tracking referrers from all subdomains of `amplitude.com`, for example, `data.amplitude.com`, `analytics.amplitude.com` and etc.
+
+In addition to excluding referrers from the default configuration, you can add other domains by setting the custom `excludeReferrers`. Custom `excludeReferrers` overrides the default values. For example, to also exclude referrers from `google.com`, set `excludeReferrers` to `[/amplitude\.com$/, 'google.com']`.
+
+### Track page views[](#track-page-views "Permalink")
+
+Amplitude tracks page view events by default. The default behavior sends a page view event on initialization. The event type for this event is `[Amplitude] Page Viewed`.
+
+Set `config.autocapture.pageViews` to `false` to disable page view tracking.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: false, 
   },
 });
-
-

Advanced configuration for tracking page views

-

Use the advanced configuration to better control when the SDK sends page view events.

-

- Tracking page views options - -

- - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.pageViews.trackOnOptional. Type: "attribution" or () => boolean. Provides advanced control for when the SDK tracks page view events. Omit or set the value to undefined, and configure the SDK to track page view events to on initialization. Set the value to "attribution" and configure the SDK to track page view events to only when it tracks web attribution. Set the value to a function that returns a boolean (true or false) and configure the SDK to track page view events to based on your criteria.
config.autocapture.pageViews.trackHistoryChangesOptional. Type: "pathOnly" or "all". Provides advanced control for single page application for when the SDK tracks page views. Omit or set the value to "all", and configure the SDK to track page view events on any navigation change to the URL within your single page application. For example: navigating from https://amplitude.com/#company to https://amplitude.com/#blog. Set the value to pathOnly, and configure the SDK to track page view events on navigation change to the URL path only within your single page application. For example: navigating from https://amplitude.com/company to https://amplitude.com/blog.
config.autocapture.pageViews.eventTypeOptional. Type: string. Customize the event_type for page view event.
-

-

-

-

For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for tracking page views[](#advanced-configuration-for-tracking-page-views "Permalink")
+
+Use the advanced configuration to better control when the SDK sends page view events.
+
+For example, you can configure Amplitude to track page views only when the URL path contains a certain substring.
+
+```ts
+amplitude.init(API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     pageViews: { 
-      trackOn: () => {
+      trackOn: () => {
         return window.location.pathname.includes('home');
       },
     },
   },
 });
-
-

Browser SDK tracks the following information in page view events.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
event_typestring. The event type for page view event. Configurable through autocapture.pageViews.eventType or enrichment plugin.[Amplitude] Page Viewed from version 1.9.1.
event_properties.[Amplitude] Page Domainstring. The page domain.location.hostnameor ''.
event_properties.[Amplitude] Page Locationstring. The page location.location.href or ''.
event_properties.[Amplitude] Page Pathstring. The page path.location.path or ''.
event_properties.[Amplitude] Page Titlestring. The page title.document.title or ''.
event_properties.[Amplitude] Page URLstring. The value of page URL.location.href.split('?')[0] or ''.
event_properties.${CampaignParam}string. The value of UTMParameters ReferrerParameters ClickIdParameters if has any.Any undefined campaignParam or undefined.
event_properties.[Amplitude] Page Counterinteger. The count of pages viewed in the session.1
event_properties.referrerstring. The full URL of the users previous page.https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2
event_properties.referring_domainstring. The domain of the page referrer. amplitude.com
-

Review this example to understand how to enrich default page view events, such as adding more properties along with page view tracking.

-

-

Warning

-If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you apply hashes to the URL of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
-

-

Page title masking

-

Amplitude lets you to mask page titles in events that include the [Amplitude] Page Title property. This protects your sensitive page title information. Use the data-amp-mask attribute on your <title> element to exclude the actual page title from this property.

-

When the <title> element has the data-amp-mask attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:

-
<head>
-  <!-- This page title will be masked in all events that capture page titles -->
-  <title data-amp-mask>John Doe - Personal Banking Dashboard</title>
-</head>
-
-
<head>
-  <!-- Works with any attribute value -->
-  <title data-amp-mask="true">Sensitive Customer Information</title>
-</head>
-
-

-

Page title masking behavior

-
    -
  • Any presence of data-amp-mask triggers masking, regardless of the attribute value.
  • -
  • Only the page title text is masked. Events are tracked as expected.
  • -
  • This affects page view events, page URL enrichment events, and any other events that include [Amplitude] Page Title.
  • -
  • This is separate from element interaction masking which uses data-amp-mask on individual elements
  • -
  • The masked value appears as ***** in your event data
- - -

Track sessions

-

Amplitude tracks session events by default. A session is the period of time a user has your website open. See How Amplitude defines sessions for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is [Amplitude] Start Session. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is [Amplitude] End Session.

-

You can opt out of tracking session events by setting config.autocapture.sessions to false. Refer to the code sample below.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    sessions: false, 
-  },
-});
-
-

Track form interactions

-

Amplitude tracks form interaction events by default. The SDK tracks [Amplitude] Form Started when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a [Amplitude] Form Submitted when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both [Amplitude] Form Started and [Amplitude] Form Submitted events.

-

Amplitude can track forms constructed with <form> tags and <input> tags nested. For example:

-
<form id="subscriber-form" name="subscriber-form" action="/subscribe">
-  <input type="text" />
-  <input type="submit" />
-</form>
-
-

Disable form interaction tracking

-

Set config.autocapture.formInteractions to false to disable form interaction tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: false, 
-  },
-});
-
-

Control form submit tracking

-

-

Minimum SDK version

-Minimum SDK version 2.34.0.
-

-

You can control when [Amplitude] Form Submitted events are tracked by passing a FormInteractionsOptions object with a shouldTrackSubmit callback.

-

By default, Amplitude tracks all form submit events. However, when a form has the novalidate attribute set, the browser submit event fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use shouldTrackSubmit to implement custom validation logic and control when Amplitude tracks the submit event.

-

The shouldTrackSubmit callback receives the form submit event and should return true to track the submit event or false to skip tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    formInteractions: {
-      shouldTrackSubmit: (event) => {
-        // Only track submit if form is valid
-        const form = event.target;
-        return form.checkValidity();
-      }
-    }
-  },
-});
-
-

Track file downloads

-

Amplitude tracks file download events by default. The SDK tracks [Amplitude] File Downloaded when the user clicks an anchor or <a> tag linked to a file. Amplitude determines that the anchor or <a> tag linked to a file if the file extension matches the following regex:

-

pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma

-

Set config.autocapture.fileDownloads to false to disable file download tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    fileDownloads: false,
-  },
-});
-
-

Track element interactions

-

You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for Visual labeling and powers Zoning for analyzing engagement within defined page areas. Review our page on Autocapture privacy and security for more information about the data collected with these events.

-

Set config.autocapture.elementInteractions to true to enable element click and change tracking.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: true, 
-  },
-});
-
-

Advanced configuration for element interactions

-

Use the advanced configuration to control element interaction tracking.

-

- Tracking element interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.elementInteractions.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should always be tracked. By default, this is set to ['a','button','input','select','textarea','label','video','audio','[contenteditable="true" i]','[data-amp-default-track]','.amp-default-track']
config.autocapture.elementInteractions.actionClickAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define which elements on the page should be tracked when the page changes (for example, a new visual element appears) or the click takes a user to a new page. By default, this is set to ['div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
config.autocapture.elementInteractions.pageUrlAllowlistOptional. Type: (string|RegExp)[]. Defines the URL, URLs, or URL pattern on which Amplitude tracks element click and change events. By default, element interactions will be captured on any URL if undefined.
config.autocapture.elementInteractions.dataAttributePrefixOptional. Type: (string|RegExp)[]. Allows the SDK to capture data attributes as an event property. By default, this is set to data-amp-track.
-

-

-

-

For example, you could configure Amplitude only to capture clicks on elements with a class of amp-tracking on the blog pages of a site as follows:

-
amplitude.init(API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
-    elementInteractions: {
-      cssSelectorAllowlist: [
-        '.amp-tracking'
-      ],
-      // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist`  [tl! ~~:2]
-      // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates.
-      actionClickAllowlist: [],
-      pageUrlAllowlist: [
-        new RegExp('https://amplitude.com/blog/*')
-      ]
-    }
-  }
-});
-
-

By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin.

-

-

Note

-When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the DEFAULT_CSS_SELECTOR_ALLOWLIST and include it in your code.

-
import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser';
+```
 
-const selectors = [
-  ...DEFAULT_CSS_SELECTOR_ALLOWLIST,
-  '.class-of-a-thing-i-want-to-track',
-];
-
-

-

-

- Element interaction events - -
-When you enable element interactions for Autocapture, Amplitude sends two events, from which you can create labeled events with visual labeling:

-
    -
  • [Amplitude] Element Clicked
  • -
  • [Amplitude] Element Changed
  • -
-

These two events capture properties that describe the corresponding element and other context about the user's browser:

- -
    -
  • [Amplitude] Element ID
  • -
  • [Amplitude] Element Class
  • -
  • [Amplitude] Element Tag
  • -
  • [Amplitude] Element Text (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Href (Collected for [Amplitude] Element Clicked, only)
  • -
  • [Amplitude] Element Position Left
  • -
  • [Amplitude] Element Position Top
  • -
  • [Amplitude] Viewport Height
  • -
  • [Amplitude] Viewport Width
  • -
  • [Amplitude] Page URL
  • -
  • [Amplitude] Page Title
  • -
  • [Amplitude] Element Selector
  • -
  • [Amplitude] Element Hierarchy
  • -
  • [Amplitude] Element Attributes
  • -
  • [Amplitude] Element Aria Label
  • -
  • [Amplitude] Element Parent Label
  • -
- -

-

-

-

Track frustration interactions

-

Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and
-"dead click" events as:

-
    -
  • Rage click: A user clicks the same element, within 50px, four times in under a second.
  • -
  • Dead click: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change.
  • -
  • Error click: A user clicks an element and a browser error occurs within two seconds of the click.
  • -
  • Thrashed cursor: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration.
  • -
-

Set config.autocapture.frustrationInteractions to true to enable capture of dead clicks and rage clicks.

-

Set config.autocapture.frustrationInteractions.rageClicks to true to enable capture of rage clicks.

-

Set config.autocapture.frustrationInteractions.deadClicks to true to enable capture of dead clicks.

-

Set config.autocapture.frustrationInteractions.errorClicks to true to enable capture of error clicks.

-

Set config.autocapture.frustrationInteractions.thrashedCursor to true to enable capture of thrashed cursors.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
-  autocapture: {
+Browser SDK tracks the following information in page view events.
+
+Name
+
+Description
+
+Default Value
+
+`event_type`
+
+`string`. The event type for page view event. Configurable through `autocapture.pageViews.eventType` or enrichment plugin.
+
+`[Amplitude] Page Viewed` from version 1.9.1.
+
+`event_properties.[Amplitude] Page Domain`
+
+`string`. The page domain.
+
+`location.hostname`or `''`.
+
+`event_properties.[Amplitude] Page Location`
+
+`string`. The page location.
+
+`location.href` or `''`.
+
+`event_properties.[Amplitude] Page Path`
+
+`string`. The page path.
+
+`location.path` or `''`.
+
+`event_properties.[Amplitude] Page Title`
+
+`string`. The page title.
+
+`document.title` or `''`.
+
+`event_properties.[Amplitude] Page URL`
+
+`string`. The value of page URL.
+
+`location.href.split('?')[0]` or `''`.
+
+`event_properties.${CampaignParam}`
+
+`string`. The value of `UTMParameters` `ReferrerParameters` `ClickIdParameters` if has any.
+
+Any undefined `campaignParam` or `undefined`.
+
+`event_properties.[Amplitude] Page Counter`
+
+`integer`. The count of pages viewed in the session.
+
+`1`
+
+`event_properties.referrer`
+
+`string`. The full URL of the users previous page.
+
+`https://amplitude.com/docs/sdks/analytics/browser/browser-sdk-2`
+
+`event_properties.referring_domain`
+
+`string`. The domain of the page referrer. `amplitude.com`
+
+Review [this example](https://github.com/amplitude/Amplitude-TypeScript/blob/main/examples/plugins/page-view-tracking-enrichment/index.ts) to understand how to enrich default page view events, such as adding more properties along with page view tracking.
+
+## Warning
+
+If you want Autocapture to include page views for multi-step forms that dynamically update and, therefore, don't refresh the URL with each step, you must use hash elements for Single Page Applications (SPAs). Autocapture doesn't capture the individual dynamic components automatically. Tools such as Google Tag Manager (GTM) can help you [apply hashes to the URL](https://support.google.com/tagmanager/answer/7679410?hl=en) of the SPA between steps. Autocapture can then ingest the different steps as users proceed through the form.
+
+#### Page title masking[](#page-title-masking "Permalink")
+
+Amplitude lets you to mask page titles in events that include the `[Amplitude] Page Title` property. This protects your sensitive page title information. Use the `data-amp-mask` attribute on your `` element to exclude the actual page title from this property.
+
+When the `<title>` element has the `data-amp-mask` attribute, Amplitude replaces the page title with a masked value across all events that capture page title information. For example:
+
+```html
+<head>
+  <!-- This page title will be masked in all events that capture page titles -->
+  <title data-amp-mask>John Doe - Personal Banking Dashboard
+
+```
+
+```html
+
+  
+  Sensitive Customer Information
+
+```
+
+## Page title masking behavior
+
+-   Any presence of `data-amp-mask` triggers masking, regardless of the attribute value.
+-   Only the page title text is masked. Events are tracked as expected.
+-   This affects page view events, page URL enrichment events, and any other events that include `[Amplitude] Page Title`.
+-   This is separate from [element interaction masking](/docs/data/autocapture#precise-text-masking) which uses `data-amp-mask` on individual elements
+-   The masked value appears as `*****` in your event data
+
+### Track sessions[](#track-sessions "Permalink")
+
+Amplitude tracks session events by default. A session is the period of time a user has your website open. See [How Amplitude defines sessions](/docs/data/sources/instrument-track-sessions) for more information. When a new session starts, Amplitude tracks a session start event and is the first event of the session. The event type for session start is `[Amplitude] Start Session`. When an existing session ends, Amplitude tracks a session end event, which is the last event of the session. The event type for session end is `[Amplitude] End Session`.
+
+You can opt out of tracking session events by setting `config.autocapture.sessions` to `false`. Refer to the code sample below.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+  autocapture: {
+    sessions: false, 
+  },
+});
+```
+
+### Track form interactions[](#track-form-interactions "Permalink")
+
+Amplitude tracks form interaction events by default. The SDK tracks `[Amplitude] Form Started` when the user initially interacts with the form element. An initial interaction can be the first change to a text input, radio button, or dropdown. The SDK tracks a `[Amplitude] Form Submitted` when the user submits the form. If a user submits a form with no initial change to any form fields, Amplitude tracks both `[Amplitude] Form Started` and `[Amplitude] Form Submitted` events.
+
+Amplitude can track forms constructed with `
` tags and `` tags nested. For example: + +```html + + + +
+``` + +#### Disable form interaction tracking[](#disable-form-interaction-tracking "Permalink") + +Set `config.autocapture.formInteractions` to `false` to disable form interaction tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: false, + }, +}); +``` + +#### Control form submit tracking[](#control-form-submit-tracking "Permalink") + +## Minimum SDK version + +Minimum SDK version 2.34.0. + +You can control when `[Amplitude] Form Submitted` events are tracked by passing a `FormInteractionsOptions` object with a `shouldTrackSubmit` callback. + +By default, Amplitude tracks all form submit events. However, when a form has the [`novalidate`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/noValidate) attribute set, the browser [submit event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event) fires without performing default validation checks. This means the submit event triggers even if the form is empty or contains invalid data. In these cases, use `shouldTrackSubmit` to implement custom validation logic and control when Amplitude tracks the submit event. + +The `shouldTrackSubmit` callback receives the form submit event and should return `true` to track the submit event or `false` to skip tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + formInteractions: { + shouldTrackSubmit: (event) => { + // Only track submit if form is valid + const form = event.target; + return form.checkValidity(); + } + } + }, +}); +``` + +### Track file downloads[](#track-file-downloads "Permalink") + +Amplitude tracks file download events by default. The SDK tracks `[Amplitude] File Downloaded` when the user clicks an anchor or `` tag linked to a file. Amplitude determines that the anchor or `` tag linked to a file if the file extension matches the following regex: + +`pdf|xlsx?|docx?|txt|rtf|csv|exe|key|pp(s|t|tx)|7z|pkg|rar|gz|zip|avi|mov|mp4|mpe?g|wmv|midi?|mp3|wav|wma` + +Set `config.autocapture.fileDownloads` to `false` to disable file download tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + fileDownloads: false, + }, +}); +``` + +### Track element interactions[](#track-element-interactions "Permalink") + +You can enable element interaction tracking to capture clicks and changes for elements on your page, which is required for [Visual labeling](/docs/data/visual-labeling) and powers [Zoning](/docs/session-replay/zoning) for analyzing engagement within defined page areas. Review our page on [Autocapture privacy and security](/docs/data/autocapture#privacy-and-security) for more information about the data collected with these events. + +Set `config.autocapture.elementInteractions` to `true` to enable element click and change tracking. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: true, + }, +}); +``` + +#### Advanced configuration for element interactions[](#advanced-configuration-for-element-interactions "Permalink") + +Use the advanced configuration to control element interaction tracking. + +For example, you could configure Amplitude only to capture clicks on elements with a class of `amp-tracking` on the blog pages of a site as follows: + +```ts +amplitude.init(API_KEY, OPTIONAL_USER_ID, { + autocapture: { + elementInteractions: { + cssSelectorAllowlist: [ + '.amp-tracking' + ], + // When you use `cssSelectorAllowlist` to target specific elements, set `actionClickAllowlist` [tl! ~~:2] + // to ensure that Amplitude tracks interactions with non-standard clickable elements during page transitions or DOM updates. + actionClickAllowlist: [], + pageUrlAllowlist: [ + new RegExp('https://amplitude.com/blog/*') + ] + } + } +}); +``` + +By default, if you don't use these settings, Amplitude tracks the default selectors on all page on which you enable the plugin. + +## Note + +When you specify the CSS selectors to track, your selection overrides the default. To retain the default selectors import the `DEFAULT_CSS_SELECTOR_ALLOWLIST` and include it in your code. + +```js +import { DEFAULT_CSS_SELECTOR_ALLOWLIST } from '@amplitude/plugin-autocapture-browser'; + +const selectors = [ + ...DEFAULT_CSS_SELECTOR_ALLOWLIST, + '.class-of-a-thing-i-want-to-track', +]; +``` + +### Track frustration interactions[](#track-frustration-interactions "Permalink") + +Enable frustration interaction tracking to capture rage clicks and dead clicks. Amplitude defines "rage click" and +"dead click" events as: + +- **Rage click**: A user clicks the same element, within 50px, four times in under a second. +- **Dead click**: A user clicks an interactable element, but no navigation change happens and the DOM doesn't change. +- **Error click**: A user clicks an element and a browser error occurs within two seconds of the click. +- **Thrashed cursor**: A user's cursor moves rapidly back and forth within a short time window, indicating potential frustration. + +Set `config.autocapture.frustrationInteractions` to `true` to enable capture of dead clicks and rage clicks. + +Set `config.autocapture.frustrationInteractions.rageClicks` to `true` to enable capture of rage clicks. + +Set `config.autocapture.frustrationInteractions.deadClicks` to `true` to enable capture of dead clicks. + +Set `config.autocapture.frustrationInteractions.errorClicks` to `true` to enable capture of error clicks. + +Set `config.autocapture.frustrationInteractions.thrashedCursor` to `true` to enable capture of thrashed cursors. + +```ts +amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, { + autocapture: { frustrationInteractions: true, }, }); -
-

Advanced configuration for frustration interactions

-

Use the advanced configuration to control frustration interaction tracking.

-

- Tracking frustration interaction options - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
config.autocapture.frustrationInteractions.deadClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures dead clicks. By default, this is set to DEFAULT_DEAD_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.rageClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures rage clicks. By default, this is set to capture on any element.
config.autocapture.frustrationInteractions.errorClicks.cssSelectorAllowlistOptional. Type: (string)[]. Accepts one or more CSS selectors that define the elements on which Amplitude captures error clicks. By default, this is set to DEFAULT_ERROR_CLICK_ALLOWLIST
config.autocapture.frustrationInteractions.thrashedCursor.directionChangesOptional. Type: number. Number of direction changes required to consider a thrashed cursor. X-axis changes and Y-axis changes are counted separately. Default is 10
config.autocapture.frustrationInteractions.thrashedCursor.thresholdOptional. Type: number. Time window (in milliseconds) that direction changes need to happen
for it to be considered a thrashed cursor. Default is 2000 (2 seconds).
-

-

-

-

Track error clicks

-

Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.

-

Enable error click tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+#### Advanced configuration for frustration interactions[](#advanced-configuration-for-frustration-interactions "Permalink")
+
+Use the advanced configuration to control frustration interaction tracking.
+
+#### Track error clicks[](#track-error-clicks "Permalink")
+
+Error click tracking captures when a user clicks an element and a browser error occurs within two seconds of the click. This helps you identify which user interactions may be triggering errors in your application.
+
+Enable error click tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       errorClicks: true,
     },
   },
 });
-
-

When you enable error click tracking, it emits an event [Amplitude] Error Click that includes these properties:

-
    -
  • [Amplitude] Kind: The type of error (one of uncaught exception, console error, unhandled promise rejection).
  • -
  • [Amplitude] Message: The error message.
  • -
  • [Amplitude] Stack: The error stack trace.
  • -
  • [Amplitude] Filename: The filename where the error occurred.
  • -
  • [Amplitude] Line Number: The line number where the error occurred.
  • -
  • [Amplitude] Column Number: The column number where the error occurred.
  • -
  • Element properties from the clicked element (for example, [Amplitude] Element Text, [Amplitude] Element Tag Name).
  • -
-

Track thrashed cursor

-

Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.

-

Enable thrashed cursor tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable error click tracking, it emits an event `[Amplitude] Error Click` that includes these properties:
+
+-   `[Amplitude] Kind`: The type of error (one of uncaught exception, console error, unhandled promise rejection).
+-   `[Amplitude] Message`: The error message.
+-   `[Amplitude] Stack`: The error stack trace.
+-   `[Amplitude] Filename`: The filename where the error occurred.
+-   `[Amplitude] Line Number`: The line number where the error occurred.
+-   `[Amplitude] Column Number`: The column number where the error occurred.
+-   Element properties from the clicked element (for example, `[Amplitude] Element Text`, `[Amplitude] Element Tag Name`).
+
+#### Track thrashed cursor[](#track-thrashed-cursor "Permalink")
+
+Thrashed cursor tracking captures when a user's cursor moves rapidly back and forth with multiple direction changes within a short time window. This helps identify areas where users may be experiencing frustration or confusion.
+
+Enable thrashed cursor tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     frustrationInteractions: {
       thrashedCursor: true,
     },
   },
 });
-
-

It emits an event called [Amplitude] Thrashed Cursor

-

Track network requests

-

Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range 500-599, excluding requests made to any amplitude.com domain.

-

Set config.autocapture.networkTracking to true to enable network request tracking

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+It emits an event called `[Amplitude] Thrashed Cursor`
+
+### Track network requests[](#track-network-requests "Permalink")
+
+Track when network requests fail (only XHR and fetch). By default, tracks network requests with a response code in the range `500-599`, excluding requests made to any `amplitude.com` domain.
+
+Set `config.autocapture.networkTracking` to `true` to enable network request tracking
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: true, 
   },
 });
-
-

When you enable this setting, Amplitude tracks the [Amplitude] Network Request event whenever the application makes a network request.

-

- Event Properties Descriptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event propertyDescription
[Amplitude] URLThe URL of the network request with sensitive information masked.
[Amplitude] URL QueryThe query parameters of the URL.
[Amplitude] URL FragmentThe fragment identifier of the URL.
[Amplitude] Request MethodThe HTTP method used for the request (GET, POST, etc.).
[Amplitude] Status CodeThe HTTP status code of the response.
[Amplitude] Error CodeThe local error code if the request failed with out a status code.
[Amplitude] Error MessageThe local error message if the request failed with out a status code.
[Amplitude] Start TimeThe timestamp when the request started, in milliseconds since Unix epoch.
[Amplitude] Completion TimeThe timestamp when the request completed, in milliseconds since Unix epoch.
[Amplitude] DurationThe duration of the request in milliseconds.
[Amplitude] Request Body SizeThe size of the request body in bytes.
[Amplitude] Response Body SizeThe size of the response body in bytes.
[Amplitude] Request BodyThe captured JSON request body (when you configure a requestBody capture rule).
[Amplitude] Response BodyThe captured JSON response body (when you configure a responseBody capture rule).
-

-

-

-

Advanced configuration for network tracking

-

Set config.autocapture.networkTracking to a NetworkTrackingOptions object to configure which network requests get tracked.

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+When you enable this setting, Amplitude tracks the `[Amplitude] Network Request` event whenever the application makes a network request.
+
+#### Advanced configuration for network tracking[](#advanced-configuration-for-network-tracking "Permalink")
+
+Set `config.autocapture.networkTracking` to a `NetworkTrackingOptions` object to configure which network requests get tracked.
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     networkTracking: {
       captureRules: [
         {
-          statusCodeRange: "400-599"
+          statusCodeRange: "400-599"
         }
       ],
-      ignoreHosts: ["*.example.com"],
+      ignoreHosts: ["*.example.com"],
       ignoreAmplitudeRequests: true
     }
   },
 });
-
-

This example tracks network requests with status codes from 400-599, ignores requests to *.example.com domains, and excludes Amplitude's own requests. Review the configuration options below for more details.

-

- NetworkTrackingOptions - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionValue
captureRulesThe rules for capturing network requests. You should always append rules with specific hosts to the bottom of the list.undefined
ignoreHostsThe hosts to ignore. Supports wildcard characters *. For example, ["*"] to ignore all hosts, ["*.notmyapi.com", "notmyapi.com"] to ignore notmyapi.com and all subdomains.[]
ignoreAmplitudeRequestsWhether to ignore Amplitude requests.true
-

-

-

-

- NetworkTrackingOptions.NetworkCaptureRule - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
urlsDefines the URL, URLs, or URL pattern to capture. By default captures all URLs. eg. [/https:\/\/example.com\/api\/*/, 'https://example.com/api/status']<any>
hostsThe hosts to capture. Supports wildcard characters *. eg. ["*"] to match all hosts, ["*.example.com", "example.com"] to match example.com and all subdomains. (this is deprecated. URLs is the preferred way to filter by hosts.)none
methodsThe HTTP methods to capture. e.g.: ["POST", "PUT", "DELETE"]['*']
statusCodeRangeThe status code range to capture. Supports comma-separated ranges or single status codes. For example, "0,200-299,413,500-599""500-599"
requestBodyCaptures fields in the request body (go to #BodyCaptureRule).undefined
responseBodyCaptures fields in the response body (go to #BodyCaptureRule).undefined
requestHeadersCaptures request headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
responseHeadersCaptures response headers. If true, captures safe headers. If false, no headers captured. If an array of strings, captures the specified headers.false
-

-

-

-

- BodyCaptureRule - -

- - - - - - - - - - - - - - - - - - - - -
NameDescriptionDefault Value
allowlistArray of JSON property names to capture from request/response bodies. Uses JSON Pointer syntax where leading / is optional. Supports wildcards: * matches any key, ** matches any number of keys. Maintains the structure of the original JSON.[]
blocklistArray of JSON property names to exclude from captured request/response bodies. This removes properties that the allowlist would otherwise capture.[]
-

-

-

-

Safe headers

-

When you set requestHeaders: true or responseHeaders: true, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.

-

- Safe headers list - -

-
    -
  • access-control-allow-origin
  • -
  • access-control-allow-credentials
  • -
  • access-control-expose-headers
  • -
  • access-control-max-age
  • -
  • access-control-allow-methods
  • -
  • access-control-allow-headers
  • -
  • accept-patch
  • -
  • accept-ranges
  • -
  • age
  • -
  • allow
  • -
  • alt-svc
  • -
  • cache-control
  • -
  • connection
  • -
  • content-disposition
  • -
  • content-encoding
  • -
  • content-language
  • -
  • content-length
  • -
  • content-location
  • -
  • content-md5
  • -
  • content-range
  • -
  • content-type
  • -
  • date
  • -
  • delta-base
  • -
  • etag
  • -
  • expires
  • -
  • im
  • -
  • last-modified
  • -
  • link
  • -
  • location
  • -
  • permanent
  • -
  • p3p
  • -
  • pragma
  • -
  • proxy-authenticate
  • -
  • public-key-pins
  • -
  • retry-after
  • -
  • server
  • -
  • status
  • -
  • strict-transport-security
  • -
  • trailer
  • -
  • transfer-encoding
  • -
  • tk
  • -
  • upgrade
  • -
  • vary
  • -
  • via
  • -
  • warning
  • -
  • www-authenticate
  • -
  • x-b3-traceid
  • -
  • x-frame-options
  • -
-

-

-

-

Network body capture

-

If a network request or response body is in JSON, you can capture part of the response body by configuring responseBody.allowlist and responseBody.blocklist. You can capture part of the request body by configuring requestBody.allowlist and requestBody.blocklist.

-

The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: ['foo/bar', 'hello/**']). allowlist tells the client which fields to capture. excludelist tells the client to exclude fields from capture (by default, nothing captured)

-

Example request/response body

-
{
-  "a": "A",
-  "b": {
-    "c": "C",
-    "d": {
-      "e": "E",
-      "f": "F"
+```
+
+This example tracks network requests with status codes from 400-599, ignores requests to `*.example.com` domains, and excludes Amplitude's own requests. Review the configuration options below for more details.
+
+#### Safe headers[](#safe-headers "Permalink")
+
+When you set `requestHeaders: true` or `responseHeaders: true`, Amplitude captures only safe headers and excludes sensitive ones that may contain authentication credentials or personally identifiable information.
+
+#### Network body capture[](#network-body-capture "Permalink")
+
+If a network request or response body is in JSON, you can capture part of the response body by configuring `responseBody.allowlist` and `responseBody.blocklist`. You can capture part of the request body by configuring `requestBody.allowlist` and `requestBody.blocklist`.
+
+The allowlist and blocklist are lists of JSON Pointer-like strings that capture specific fields. (For example: `['foo/bar', 'hello/**']`). `allowlist` tells the client which fields to capture. `excludelist` tells the client to exclude fields from capture (by default, nothing captured)
+
+Example request/response body
+
+```json
+{
+  "a": "A",
+  "b": {
+    "c": "C",
+    "d": {
+      "e": "E",
+      "f": "F"
     }
   },
-  "g": "G"
+  "g": "G"
 }
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
allowlistCaptured Result
a{ "a": "A" }
a/b/*{ "a": { "b": { "c": "C" } } }
b/c{ "b": { "c": "C" } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }
b/d/*{ "b": { "d": { "e": "E", "f": "F" } } }
b/**{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }
*{ "a": "A", "g": "G" }
-

Track web vitals

-

Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as [Amplitude] Web Vitals events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).

-

-

Note

-Requires Browser SDK 2.27.0 or higher.
-

-

Set config.autocapture.webVitals to true to enable web vitals tracking:

-
amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
+```
+
+allowlist
+
+Captured Result
+
+`a`
+
+`{ "a": "A" }`
+
+`a/b/*`
+
+`{ "a": { "b": { "c": "C" } } }`
+
+`b/c`
+
+`{ "b": { "c": "C" } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } } }`
+
+`b/d/*`
+
+`{ "b": { "d": { "e": "E", "f": "F" } } }`
+
+`b/**`
+
+`{ "b": { "c": "C", "d": { "e": "E", "f": "F" } }`
+
+`*`
+
+`{ "a": "A", "g": "G" }`
+
+### Track web vitals[](#track-web-vitals "Permalink")
+
+Track Core Web Vitals performance metrics automatically. When enabled, Amplitude captures web performance metrics and sends them as `[Amplitude] Web Vitals` events when the browser tab first becomes hidden (when users navigate away, close the tab, or switch tabs).
+
+## Note
+
+Requires Browser SDK 2.27.0 or higher.
+
+Set `config.autocapture.webVitals` to `true` to enable web vitals tracking:
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, OPTIONAL_USER_ID, {
   autocapture: {
     webVitals: true, //[tl! highlight]
   },
 });
-
-

Metrics captured

-

The web vitals autocapture feature captures the following Core Web Vitals metrics

- -

Event properties

-

The [Amplitude] Web Vitals event includes the following properties:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PropertyDescription
[Amplitude] Page DomainThe hostname of the current page
[Amplitude] Page LocationThe full URL of the current page
[Amplitude] Page PathThe pathname of the current page
[Amplitude] Page TitleThe title of the current page
[Amplitude] Page URLThe URL of the current page without query parameters
[Amplitude] LCPLargest Contentful Paint (if available)
[Amplitude] FCPFirst Contentful Paint (if available)
[Amplitude] INPInteraction to Next Paint (if available)
[Amplitude] CLSCumulative Layout Shift (if available)
[Amplitude] TTFBTime to First Byte (if available)
-

Track an event

-

Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.

-
// Track a basic event.
+```
+
+#### Metrics captured[](#metrics-captured "Permalink")
+
+The web vitals autocapture feature captures the following Core Web Vitals metrics
+
+-   [INP](https://web.dev/articles/inp)
+-   [TTFB](https://web.dev/articles/ttfb)
+-   [LCP](https://web.dev/articles/lcp)
+-   [FCP](https://web.dev/articles/fcp)
+-   [CLS](https://web.dev/articles/cls)
+
+#### Event properties[](#event-properties "Permalink")
+
+The `[Amplitude] Web Vitals` event includes the following properties:
+
+Property
+
+Description
+
+`[Amplitude] Page Domain`
+
+The hostname of the current page
+
+`[Amplitude] Page Location`
+
+The full URL of the current page
+
+`[Amplitude] Page Path`
+
+The pathname of the current page
+
+`[Amplitude] Page Title`
+
+The title of the current page
+
+`[Amplitude] Page URL`
+
+The URL of the current page without query parameters
+
+`[Amplitude] LCP`
+
+[Largest Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#lcpmetric) (if available)
+
+`[Amplitude] FCP`
+
+[First Contentful Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#fcpmetric) (if available)
+
+`[Amplitude] INP`
+
+[Interaction to Next Paint](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#inpmetric) (if available)
+
+`[Amplitude] CLS`
+
+[Cumulative Layout Shift](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#clsmetric) (if available)
+
+`[Amplitude] TTFB`
+
+[Time to First Byte](https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#ttfbmetric) (if available)
+
+## Track an event[](#track-an-event "Permalink")
+
+Events represent how users interact with your application. For example, the Button Clicked event might be an action you want to track.
+
+```ts
+// Track a basic event.
 amplitude.track('Button Clicked');
 
 // Track events with optional properties.
@@ -3008,548 +889,654 @@ const eventProperties = {
   buttonColor: 'primary',
 };
 amplitude.track('Button Clicked', eventProperties);
-
-

You can also pass a BaseEvent object to track. For more information, review the BaseEvent interface for all available fields.

-
const event_properties = {
+```
+
+You can also pass a `BaseEvent` object to `track`. For more information, review the [BaseEvent](https://amplitude.github.io/Amplitude-TypeScript/interfaces/_amplitude_analytics_browser.Types.BaseEvent.html) interface for all available fields.
+
+```ts
+const event_properties = {
   buttonColor: 'primary',
 };
 
 const event = {
-  event_type: "Button Clicked", 
+  event_type: "Button Clicked", 
   event_properties,
   groups: { 'role': 'engineering' },
   group_properties: { 'groupPropertyKey': 'groupPropertyValue' }
 };
 
 amplitude.track(event);
-
-

Track events to multiple projects

-

By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent apiKey, userId, deviceId, and settings values.

-
const defaultInstance = amplitude.createInstance();
+```
+
+## Track events to multiple projects[](#track-events-to-multiple-projects "Permalink")
+
+By default, Amplitude SDKs send data to one Amplitude project. To send data to more than one project, add an instance of the Amplitude SDK for each project you want to receive data. Then, pass instance variables to wherever you want to call Amplitude. Each instance allows for independent `apiKey`, `userId`, `deviceId`, and `settings` values.
+
+```ts
+const defaultInstance = amplitude.createInstance();
 defaultInstance.init(API_KEY_DEFAULT);
 
 const envInstance = amplitude.createInstance();
 envInstance.init(API_KEY_ENV, {
   instanceName: 'env',
 });
-
-

User properties

-

User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.

-

Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations set, setOnce, unset, add, append, prepend, preInsert, postInsert, remove, and clearAll on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.

-

-

Identify calls

-If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see Overview of user properties and event properties.
-

-

Set a user property

-

The Identify object provides controls for setting user properties. To set a user property:

-
    -
  1. Instantiate an Identify object
  2. -
  3. Call methods on that object
  4. -
  5. Instruct the SDK to make a call with the Identify object
  6. -
-
const identifyEvent = new amplitude.Identify();
+```
+
+## User properties[](#user-properties "Permalink")
+
+User properties are details like device details, user preferences, or language to help you understand your users at the time they performed an action in your app.
+
+Identify is for setting the user properties of a particular user without sending any event. The SDK supports the operations `set`, `setOnce`, `unset`, `add`, `append`, `prepend`, `preInsert`, `postInsert`, `remove`, and `clearAll` on individual user properties. Declare the operations through a provided Identify interface. You can chain together multiple operations in a single Identify object. The Identify object is then passed to the Amplitude client to send to the server.
+
+## Identify calls
+
+If the SDK sends the Identify call after the event, the details of the call appear immediately in the user's profile in Amplitude. Results don't appear in chart results until the SDK sends another event after Identify. Identify calls affect events that happen after it. For more information, see [Overview of user properties and event properties](/docs/data/user-properties-and-events).
+
+### Set a user property[](#set-a-user-property "Permalink")
+
+The Identify object provides controls for setting user properties. To set a user property:
+
+1.  Instantiate an Identify object
+2.  Call methods on that object
+3.  Instruct the SDK to make a call with the Identify object
+
+```ts
+const identifyEvent = new amplitude.Identify();
 // Use methods in the following sections to update the Identify object
 amplitude.identify(identifyEvent);
-
-

Identify.set

-

This method sets the value of a user property. For example, you can set a role property of a user.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.set[](#identifyset "Permalink")
+
+This method sets the value of a user property. For example, you can set a role property of a user.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.set('location', 'LA'); 
 amplitude.identify(identifyEvent);
-
-

Identify.setOnce

-

This method sets the value of a user property only one time. Subsequent calls using setOnce() are ignored. For example, you can set an initial login method for a user. setOnce() ignores later calls.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.setOnce[](#identifysetonce "Permalink")
+
+This method sets the value of a user property only one time. Subsequent calls using `setOnce()` are ignored. For example, you can set an initial login method for a user. `setOnce()` ignores later calls.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.setOnce('initial-location', 'SF'); 
 identify(identifyEvent);
-
-

Identify.add

-

This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to 0 before it's incremented. For example, you can track a user's travel count.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.add[](#identifyadd "Permalink")
+
+This method increments a user property by a numerical value. If the user property doesn't have a value set yet, it's initialized to `0` before it's incremented. For example, you can track a user's travel count.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.add('travel-count', 1); 
 amplitude.identify(identifyEvent);
-
-

Identify.unset

-

This method removes a user property from a user profile. Use unset when you no longer need a property or want to remove it completely.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.unset[](#identifyunset "Permalink")
+
+This method removes a user property from a user profile. Use `unset` when you no longer need a property or want to remove it completely.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.unset('location'); 
 amplitude.identify(identifyEvent);
-
-

Arrays in user properties

-

Call the prepend, append, preInsert, or postInsert methods to use arrays as user properties.

-

Identify.prepend

-

This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new Identify();
+```
+
+### Arrays in user properties[](#arrays-in-user-properties "Permalink")
+
+Call the `prepend`, `append`, `preInsert`, or `postInsert` methods to use arrays as user properties.
+
+#### Identify.prepend[](#identifyprepend "Permalink")
+
+This method prepends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new Identify();
 identifyEvent.prepend('visited-locations', 'LAX'); 
 identify(identifyEvent);
-
-

Identify.append

-

This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.append[](#identifyappend "Permalink")
+
+This method appends a value or values to a user property array. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are prepended.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.append('visited-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.postInsert

-

This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.postInsert[](#identifypostinsert "Permalink")
+
+This method post-inserts a value or values to a user property if it doesn't exist in the user property yet. Post-insert means inserting the values at the end of a given list. If the user property doesn't have a value set yet, it's initialized to an empty list before the new values are post-inserted. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.postInsert('unique-locations', 'SFO'); 
 amplitude.identify(identifyEvent);
-
-

Identify.remove

-

This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.remove[](#identifyremove "Permalink")
+
+This method removes a value or values to a user property if it exists in the user property. Remove means remove the existing values from the given list. If the user property has an existing value, this method is a no-op.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.remove('unique-locations', 'JFK') 
 amplitude.identify(identifyEvent);
-
-

Identify.clearAll

-

This method removes all user properties from the user. Use clearAll with care because it's irreversible.

-
const identifyEvent = new amplitude.Identify();
+```
+
+#### Identify.clearAll[](#identifyclearall "Permalink")
+
+This method removes all user properties from the user. Use `clearAll` with care because it's irreversible.
+
+```ts
+const identifyEvent = new amplitude.Identify();
 identifyEvent.clearAll();
 amplitude.identify(identifyEvent);
-
-

User groups

-

Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.

-

For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.

-

When setting groups, define a groupType and groupName. In the previous example, 'orgId' is the groupType and '10' and '15' are the values for groupName. Another example of a groupType could be 'sport' with groupName values like 'tennis' and 'baseball'.

-

Setting a group also sets the groupType:groupName as a user property, and overwrites any existing groupName value set for that user's groupType, and the corresponding user property value. groupType is a string, and groupName can be either a string or an array of strings to tell that a user is in multiple groups.

-

-

Example

-If Joe is in 'orgId' '15', then the groupName is 15.

-
// set group with a single group name
+```
+
+## User groups[](#user-groups "Permalink")
+
+Amplitude supports assigning users to groups and performing queries, such as Count by Distinct, on those groups. If at least one member of the group has performed the specific event, then the count includes the group.
+
+For example, you want to group your users based on what organization they're in by using an 'orgId'. Joe is in 'orgId' '10', and Sue is in 'orgId' '15'. Sue and Joe both perform a certain event. You can query their organizations in the Event Segmentation Chart.
+
+When setting groups, define a `groupType` and `groupName`. In the previous example, 'orgId' is the `groupType` and '10' and '15' are the values for `groupName`. Another example of a `groupType` could be 'sport' with `groupName` values like 'tennis' and 'baseball'.
+
+Setting a group also sets the `groupType:groupName` as a user property, and overwrites any existing `groupName` value set for that user's `groupType`, and the corresponding user property value. `groupType` is a string, and `groupName` can be either a string or an array of strings to tell that a user is in multiple groups.
+
+## Example
+
+If Joe is in 'orgId' '15', then the `groupName` is `15`.
+
+```ts
+// set group with a single group name
 amplitude.setGroup('orgId', '15');
-
-

If Joe is in 'sport' 'soccer' and 'tennis', then the groupName is ["tennis", "soccer"].

-
// set group with multiple group names
+```
+
+If Joe is in 'sport' 'soccer' and 'tennis', then the `groupName` is `["tennis", "soccer"]`.
+
+```ts
+// set group with multiple group names
 amplitude.setGroup('sport', ['soccer', 'tennis']);
-
-

-

-

Pass an Event object with groups to a Track call to set an event-level group. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with setGroup.

-
amplitude.track({
+```
+
+Pass an `Event` object with `groups` to a Track call to set an **event-level group**. With event-level groups, the group designation applies only to the specific logged event, and doesn't persist to the user unless you explicitly set it with `setGroup`.
+
+```ts
+amplitude.track({
   event_type: 'event type',
   event_properties: { eventPropertyKey: 'event property value' },
   groups: { 'orgId': '15' }
 })
-
-

Group properties

-

Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.

-

The groupIdentify() method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.

-
const groupType = 'plan';
+```
+
+## Group properties[](#group-properties "Permalink")
+
+Use the Group Identify API to set or update the properties of particular groups. These updates only affect events going forward.
+
+The `groupIdentify()` method accepts a group type and group name string parameter, as well as an Identify object that's applied to the group.
+
+```ts
+const groupType = 'plan';
 const groupName = 'enterprise';
 const groupIdentifyEvent = new amplitude.Identify()
 groupIdentifyEvent.set('key1', 'value1');
 amplitude.groupIdentify(groupType, groupName, groupIdentifyEvent); 
-
-

Track revenue

-

The preferred method of tracking revenue for a user is to use revenue() in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like revenueType and productId) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into revenue() to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.

-

-

Tip

-Amplitude recommends to also enable product array tracking method to get the most information possible.
-

-

To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.

-
const event = new amplitude.Revenue()
+```
+
+## Track revenue[](#track-revenue "Permalink")
+
+The preferred method of tracking revenue for a user is to use `revenue()` in conjunction with the provided Revenue interface. Revenue instances store each revenue transaction and allow you to define several special revenue properties (like `revenueType` and `productId`) that are used in Amplitude's Event Segmentation and Revenue LTV charts. These Revenue instance objects are then passed into `revenue()` to send as revenue events to Amplitude. This lets Amplitude automatically display data relevant to revenue in the platform. You can use this to track both in-app and non-in-app purchases.
+
+## Tip
+
+Amplitude recommends to also enable [product array](/docs/analytics/charts/cart-analysis) tracking method to get the most information possible.
+
+To track revenue from a user, call revenue each time a user generates revenue. In this example, the user purchased 3 units of a product at $3.99.
+
+```ts
+const event = new amplitude.Revenue()
   .setProductId('com.company.productId')
   .setPrice(3.99)
   .setQuantity(3)
   .setRevenueType('purchase');
 
-amplitude.revenue(event);
-
-

This example shows tracking revenue with additional properties:

-
const event = new amplitude.Revenue()
-  .setProductId('com.company.productId')
-  .setPrice(3.99)
-  .setQuantity(3)
-  .setRevenueType('purchase')
-  .setEventProperties({
-    category: 'electronics',
-    brand: 'Acme'
-  });
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with currency type:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setCurrency('JPY');
+
+amplitude.revenue(event);
+```
+
+This example shows tracking revenue with additional properties:
+
+```ts
+const event = new amplitude.Revenue()
+  .setProductId('com.company.productId')
+  .setPrice(3.99)
+  .setQuantity(3)
+  .setRevenueType('purchase')
+  .setEventProperties({
+    category: 'electronics',
+    brand: 'Acme'
+  });
+
+amplitude.revenue(event);
+```
+
+### Revenue interface[](#revenue-interface "Permalink")
+
+Revenue objects support the following properties. Use the corresponding setter methods to assign values.
+
+Name
+
+Setter Method
+
+Description
+
+Default Value
+
+`productId`
+
+`setProductId()`
+
+Optional. `string`. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.
+
+Empty string.
+
+`quantity`
+
+`setQuantity()`
+
+Required. `number`. The quantity of products purchased. `revenue = quantity * price`.
+
+`1`
+
+`price`
+
+`setPrice()`
+
+Required. `number`. The price of the products purchased, and this can be negative. `revenue = quantity * price`.
+
+`null`
+
+`revenueType`
+
+`setRevenueType()`
+
+Optional, but required for revenue verification. `string`. The revenue type (for example, tax, refund, income).
+
+`null`
+
+`currency`
+
+`setCurrency()`
+
+Optional. `string`. The currency type for the revenue (for example, `'USD'`, `'JPY'`, `'EUR'`).
+
+`null`
+
+`receipt`
+
+`setReceipt()`
+
+Optional. `string`. The receipt identifier of the revenue.
+
+`null`
+
+`receiptSignature`
+
+`setReceiptSignature()`
+
+Optional, but required for revenue verification. `string`. The receipt signature of the revenue.
+
+`null`
+
+`eventProperties`
+
+`setEventProperties()`
+
+Optional. `{ [key: string]: any }`. An object of event properties to include in the revenue event.
+
+`null`
+
+## Flush the event buffer[](#flush-the-event-buffer "Permalink")
+
+The `flush` method triggers the client to send buffered events immediately.
+
+```ts
+amplitude.flush();
+```
+
+By default, Browser SDK calls`flush` automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:
+
+```ts
+amplitude.init(API\_KEY).promise.then(function() {
+  amplitude.track('Button Clicked');
+  amplitude.flush();
+});
+```
+
+## Custom user identifier[](#custom-user-identifier "Permalink")
+
+If your application has a login system that you want to track users with, call `setUserId` to update the user's identifier.
+
+```ts
+amplitude.setUserId('user@amplitude.com');
+```
+
+## Custom session identifier[](#custom-session-identifier "Permalink")
+
+Assign a new session ID with `setSessionId`. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
+
+```ts
+amplitude.setSessionId(Date.now());
+```
+
+## Custom device identifier[](#custom-device-identifier "Permalink")
+
+Assign a new device ID with `deviceId`. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.
+
+```ts
+amplitude.setDeviceId(uuid());
+```
+
+## Reset when the user logs out[](#reset-when-the-user-logs-out "Permalink")
+
+Use `reset` as a shortcut to anonymize users after they log out. `reset` does the following:
+
+1.  Sets `userId` to `undefined`.
+2.  Sets `deviceId` to a new UUID value.
+
+With an undefined `userId` and a new `deviceId`, the user appears to Amplitude as a new user.
+
+```ts
+amplitude.reset();
+```
+
+## Opt users out of tracking[](#opt-users-out-of-tracking "Permalink")
+
+Set `setOptOut` to `true` to disable logging for a specific user.
+
+```ts
+amplitude.setOptOut(true);
+```
+
+Amplitude doesn't save or send events to the server while `setOptOut` is enabled. The setting persists across page loads.
+
+Set `setOptOut` to `false` to re-enable logging.
+
+```ts
+amplitude.setOptOut(false);
+```
+
+## Optional tracking[](#optional-tracking "Permalink")
+
+By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called `trackingOptions` when initializing the SDK, setting the appropriate options to false.
+
+Tracking Options
+
+Default
+
+`ipAddress`
+
+`true`
+
+`language`
+
+`true`
 
-amplitude.revenue(event);
-
-

Revenue interface

-

Revenue objects support the following properties. Use the corresponding setter methods to assign values.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameSetter MethodDescriptionDefault Value
productIdsetProductId()Optional. string. An identifier for the product. Amplitude recommends something like the Google Play Store product ID.Empty string.
quantitysetQuantity()Required. number. The quantity of products purchased. revenue = quantity * price.1
pricesetPrice()Required. number. The price of the products purchased, and this can be negative. revenue = quantity * price.null
revenueTypesetRevenueType()Optional, but required for revenue verification. string. The revenue type (for example, tax, refund, income).null
receiptsetReceipt()Optional. string. The receipt identifier of the revenue.null
receiptSignaturesetReceiptSignature()Optional, but required for revenue verification. string. The receipt signature of the revenue.null
eventPropertiessetEventProperties()Optional. { [key: string]: any }. An object of event properties to include in the revenue event.null
-

Flush the event buffer

-

The flush method triggers the client to send buffered events immediately.

-
amplitude.flush();
-
-

By default, Browser SDK callsflush automatically at an interval. If you want to flush all events, control the async flow with the optional Promise interface, for example:

-
amplitude.init(API\_KEY).promise.then(function() {
-  amplitude.track('Button Clicked');
-  amplitude.flush();
-});
-
-

Custom user identifier

-

If your application has a login system that you want to track users with, call setUserId to update the user's identifier.

-
amplitude.setUserId('user@amplitude.com');
-
-

Custom session identifier

-

Assign a new session ID with setSessionId. When you set a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).

-
amplitude.setSessionId(Date.now());
-
-

Custom device identifier

-

Assign a new device ID with deviceId. When you set a custom device ID, make sure the value is sufficiently unique. Amplitude recommends using a UUID.

-
amplitude.setDeviceId(uuid());
-
-

Reset when the user logs out

-

Use reset as a shortcut to anonymize users after they log out. reset does the following:

-
    -
  1. Sets userId to undefined.
  2. -
  3. Sets deviceId to a new UUID value.
  4. -
-

With an undefined userId and a new deviceId, the user appears to Amplitude as a new user.

-
amplitude.reset();
-
-

Opt users out of tracking

-

Set setOptOut to true to disable logging for a specific user.

-
amplitude.setOptOut(true);
-
-

Amplitude doesn't save or send events to the server while setOptOut is enabled. The setting persists across page loads.

-

Set setOptOut to false to re-enable logging.

-
amplitude.setOptOut(false);
-
-

Optional tracking

-

By default, the SDK tracks these properties automatically. You can override this behavior by passing a configuration called trackingOptions when initializing the SDK, setting the appropriate options to false.

- - - - - - - - - - - - - - - - - - - - - -
Tracking OptionsDefault
ipAddresstrue
languagetrue
platformtrue
-
amplitude.init(AMPLITUDE_API_KEY, {
+`platform`
+
+`true`
+
+```ts
+amplitude.init(AMPLITUDE_API_KEY, {
   trackingOptions: {
     ipAddress: false,
     language: false,
     platform: false,
   },
 });
-
-

Callback

-

All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.

-

-

-
-
- -

-
amplitude.init("apikey", "12321.com").promise.then(function() { 
- // init callback
-})
-

amplitude.track('Button Clicked').promise.then(function(result) { -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -}); -

-

-

-

-
// Using async/await
-const initResult = await amplitude.init("apikey", "12321.com").promise;
-

const results = await amplitude.track('Button Clicked').promise; -result.event; // {...} (The final event object sent to Amplitude) -result.code; // 200 (The HTTP response status code of the request. -result.message; // "Event tracked successfully" (The response message) -

-

-

-

-

Plugins

-

Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an Object with optional fields name and type and methods setup(), execute() and teardown().

-

add

-

The add method adds a plugin to Amplitude.

-
amplitude.add(new Plugin());
-
-

remove

-

The remove method removes the given plugin name from the client instance if it exists.

-
amplitude.remove(plugin.name);
-
-

Create a custom plugin

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Field / FunctionDescription
plugin.nameOptional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
plugin.typeOptional. The type field is an optional property that defines the type of plugin you are creating. See plugin.execute() function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
plugin.setup()Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
plugin.execute()Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.

For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: event (BaseEvent), code (number), and message (string). This is useful for sending events for third-party endpoints.
plugin.teardown()Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
-

Plugin examples

-

-

-
-
- -
-Here's an example of an enrichment plugin that includes an extra event property page_url to all events.

-
const enrichPageUrlPlugin = (): EnrichmentPlugin => {
-  return {
-    execute: async (event: Event) => {
-      event.event_properties = {
-        ...event.event_properties,
-        page_url: location.href,
-      };
-      return event;
-    },
-  }
-}
-

amplitude.add(enrichPageUrlPlugin()); -amplitude.init(API_KEY); -

-

-

-
-Here's an example of a destination plugin that sends each tracked event to a custom server URL using Fetch API.

-
const customDestination = (customUrl: string): DestinationPlugin => {
-  return {
-    type: 'destination',
-    execute: async (event: Event) => {
-      const payload = {
-        k: 'apikey',
-        d: event,
-      };
-
  const response = await fetch(customUrl, {
-    method: 'POST',
-    headers: {
-      'Content-Type': 'application/json',
-      Accept: '\*/\*',
-    },
-    body: JSON.stringify(payload),
-  });
-
-  return {
-    code: response.status,
-    event: event,
-    message: response.statusText,
-  };
-},
-
-

}; -};

-

amplitude.init(API_KEY); -amplitude.add(myDestinationPlugin('https://custom.url.com')); -

-

-

-

-

Available plugins

-

Amplitude provides several official plugins to extend the Browser SDK functionality:

-

Page URL enrichment plugin

-

The page URL enrichment plugin is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.

-

To disable page URL enrichment, set autocapture.pageUrlEnrichment to false:

-
amplitude.init(API_KEY, {
+```
+
+## Callback[](#callback "Permalink")
+
+All asynchronous APIs are optionally awaitable through a Promise interface. This also serves as a callback interface.
+
+## Plugins[](#plugins "Permalink")
+
+Plugins allow you to extend Amplitude SDK's behavior by, for example, modifying event properties (enrichment plugin) or sending to third-party endpoints (destination plugin). A plugin is an `Object` with optional fields `name` and `type` and methods `setup()`, `execute()` and `teardown()`.
+
+### add[](#add "Permalink")
+
+The `add` method adds a plugin to Amplitude.
+
+```ts
+amplitude.add(new Plugin());
+```
+
+### remove[](#remove "Permalink")
+
+The `remove` method removes the given plugin name from the client instance if it exists.
+
+```ts
+amplitude.remove(plugin.name);
+```
+
+### Create a custom plugin[](#create-a-custom-plugin "Permalink")
+
+Field / Function
+
+Description
+
+`plugin.name`
+
+Optional. The name field is an optional property that allows you to reference the plugin for deletion purposes. If not provided, Amplitude assigns a random name when you add the plugin. If you don't plan to delete your plugin, you can skip assigning a name.
+
+`plugin.type`
+
+Optional. The type field is an optional property that defines the type of plugin you are creating. See `plugin.execute()` function below to distinguish the two types. If not defined, the plugin defaults to an enrichment type.
+
+`plugin.setup()`
+
+Optional. The setup function is an optional method called when you add the plugin or on first init whichever happens later. This function accepts two parameters: 1) Amplitude configuration; and 2) Amplitude instance. This is useful for setup operations and tasks that depend on either the Amplitude configuration or instance. Examples include assigning baseline values to variables, setting up event listeners, and many more.
+
+`plugin.execute()`
+
+Optional for type:enrichment. For enrichment plugins, execute function is an optional method called on each event. This function must return a new event, otherwise, the SDK drops the passed event from the queue. This is useful for cases where you need to add/remove properties from events, filter events, or perform any operation for each event tracked.  
+  
+For destination plugins, execute function is a required method called on each event. This function must return a response object with keys: `event` (BaseEvent), `code` (number), and `message` (string). This is useful for sending events for third-party endpoints.
+
+`plugin.teardown()`
+
+Optional. The teardown function is an optional method called when Amplitude re-initializes. This is useful for resetting unneeded persistent state created/set by setup or execute methods. Examples include removing event listeners or mutation observers.
+
+### Plugin examples[](#plugin-examples "Permalink")
+
+### Available plugins[](#available-plugins "Permalink")
+
+Amplitude provides several official plugins to extend the Browser SDK functionality:
+
+#### Page URL enrichment plugin[](#page-url-enrichment-plugin "Permalink")
+
+The [page URL enrichment plugin](/docs/sdks/analytics/browser/page-url-enrichment-plugin) is enabled by default with autocapture. It automatically adds page URL-related properties to all events, including current page information, previous page location, and page type classification.
+
+To disable page URL enrichment, set `autocapture.pageUrlEnrichment` to `false`:
+
+```ts
+amplitude.init(API_KEY, {
   autocapture: {
     pageUrlEnrichment: false,
   },
 });
-
-

For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:

-
import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
+```
+
+For custom configuration or if you disabled autocapture entirely, you can still add the plugin manually:
+
+```ts
+import { pageUrlEnrichmentPlugin } from '@amplitude/plugin-page-url-enrichment-browser';
 
 const pageUrlEnrichment = pageUrlEnrichmentPlugin();
 amplitude.add(pageUrlEnrichment);
 amplitude.init(API_KEY);
-
-

Troubleshooting and debugging

-

Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.

-

Console

-

You can find JavaScript errors under Inspect > Console, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.

-
    -
  • -

    Enable debug mode by following these instructions. Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.

    -
  • -
  • -

    Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering amplitude.init(API_KEY, 'USER_ID') in the browser console, it indicates that your amplitude.init call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."

    -
  • -
-

Network Request

-

Use the Inspect > Network tab to view all network requests made by your page. Search for the Amplitude request.

-

Check the response code and ensure that the response payload is as expected.

-

Instrumentation Explorer/Chrome Extension

-

The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.

-

Common Issues

-

The following are common issues specific to Browser SDK. For more general common issues, see SDK Troubleshooting and Debugging.

-

Ad blocker

-

Ad Blocker might lead to event dropping. The following errors indicate that the tracking has been affected by Ad Blocker. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.

-
    -
  • Chrome (Ubuntu, MacOS)
    -Console: error net::ERR_BLOCKED_BY_CLIENT
    -Network: status (blocked:other)
  • -
  • Firefox (Ubuntu)
    -Console: error text doesn’t contain any blocking-specific info
    -Network: Transferred column contains the name of plugin Blocked by uBlock Origin
  • -
  • Safari (MacOS)
    -Console: error contains text Content Blocker prevented frame ... from loading a resource from ...
    -Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
  • -
-

Amplitude recommends using a proxy server to avoid this situation.

- -

Here is the information SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.

-

CORS

-

Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used setServerURL.

-

Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

-

Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the Access-Control-Allow-Origin header in the server's response.

-
    -
  • -

    If you have control over the server, you can "Update the server's CORS policy". Add the Access-Control-Allow-Origin header to the server's responses. This would allow your origin to make requests. The value of Access-Control-Allow-Origin can be * to allow all origins, or it can be the specific URL of your web page.

    -
  • -
  • -

    If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the Access-Control-Allow-Origin header to the response before sending it back to the web page.

    -
  • -
-

If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.

-

Events fired but no network requests

-

If you set the logger to "Debug" level, and see track calls in the developer console, the track() method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful track() call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.

-

There are two ways to address this issue:

-
    -
  1. -

    If you use standard network requests, set the transport to beacon during initialization or set the transport to beacon upon page exit. sendBeacon doesn't work in this case because it sends events in the background, and doesn't return server responses like 4xx or 5xx. As a result, it doesn't retry on failure. sendBeacon sends only scheduled requests in the background. For more information, see the sendBeacon section.

    -
  2. -
  3. -

    To make track() synchronous, add the await keyword before the call.

    -
  4. -
-

Advanced topics

-

Cross-domain tracking

-

You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.

-

-

Note

-Starting from v2.8.0 the SDK supports getting the device ID from the URL parameter ampDeviceId. The SDK configuration, for example, init('API_KEY', { deviceId: 'custom-device-id' }) still takes precedence over the URL parameter. Previous versions of the SDK supported the deviceId URL parameter, this option is still supported for backward compatibility but ampDeviceId will take precedence if both are set. You don't need to change your code if upgrade to versions higher than v2.8.0 but it is recommended.
-

-

For example:

-
    -
  • Site 1: www.example.com
  • -
  • Site 2: www.example.org
  • -
-

Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.
-The SDK can parse the URL parameter automatically if deviceId is in the URL query parameters.

-

Starting from v2.8.0, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.

-
    -
  1. From Site 1, grab the device ID from getDeviceId() and the session ID from getSessionId().
  2. -
  3. Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: www.example.com?ampDeviceId=device_id_from_site_1&ampSessionId=1716245958483)
  4. -
  5. Initialize the Amplitude SDK on Site 2 with init('API_KEY', null).
  6. -
-

If the deviceId and sessionId aren't set in init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 }), the SDK automatically falls back to using the URL parameters respectively.

-

Evaluation window with ampTimestamp

-

-

Note

-This feature requires @amplitude/analytics-browser@2.21.1 and above.
-

-

To improve security and prevent the use of stale session or device IDs, you can include an ampTimestamp parameter that acts as an evaluation window. The SDK only uses ampSessionId and ampDeviceId URL parameters if the ampTimestamp value is in the future (greater than the current time).

-

For example:

-
www.example.com?ampDeviceId=device_id&ampSessionId=session_id&ampTimestamp=1640995500000
-
-

When ampTimestamp expires (is less than the current time), the SDK ignores the ampSessionId and ampDeviceId parameters. It falls back to generating new values or using stored values from cookies. If ampTimestamp isn't provided, the SDK behaves as before for backward compatibility.

-

This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.

-

Amplitude recommends that you follow the same session ID format as the Browser SDK using Date.now() because the SDK checks if an event is in session every time it tracks an event. For example:

-
// if session ID is set to 12345
-// https://www.example.com?ampDeviceId=my-device-id&ampSessionId=12345
+```
+
+## Troubleshooting and debugging[](#troubleshooting-and-debugging "Permalink")
+
+Debugging in a browser can help you identify problems related to your code's implementation, as well as potential issues within the SDKs you're using. Here's a basic guide on how to use the browser's built-in Developer Tools (DevTools) for debugging.
+
+### Console[](#console "Permalink")
+
+You can find JavaScript errors under **Inspect > Console**, which might have the details about the line of code and file that caused the problem. The console also allows you to execute JavaScript code in real time.
+
+-   Enable debug mode by following these [instructions](#debugging). Then With the default logger, extra function context information will be output to the developer console when any SDK public method is invoked, which can be helpful for debugging.
+    
+-   Amplitude supports SDK deferred initialization. Events tracked before initialization will be dispatched after the initialization call. If you cannot send events but can send the event successfully after entering `amplitude.init(API_KEY, 'USER_ID')` in the browser console, it indicates that your `amplitude.init` call might not have been triggered in your codebase or you aren't using the correct Amplitude instance during initialization. Therefore, check your implementation."
+    
+
+### Network Request[](#network-request "Permalink")
+
+Use the **Inspect > Network** tab to view all network requests made by your page. Search for the Amplitude request.
+
+Check the response code and ensure that the response payload is as expected.
+
+### Instrumentation Explorer/Chrome Extension[](#instrumentation-explorerchrome-extension "Permalink")
+
+The Amplitude Instrumentation Explorer is an extension available in the Google Chrome Web Store. The extension captures each Amplitude event you trigger and displays it in the extension popup. It's important to ensure that the event has been sent out successfully and to check the context in the event payload.
+
+### Common Issues[](#common-issues "Permalink")
+
+The following are common issues specific to Browser SDK. For more general common issues, see [SDK Troubleshooting and Debugging](/docs/sdks/sdk-debugging).
+
+#### Ad blocker[](#ad-blocker "Permalink")
+
+`Ad Blocker` might lead to event dropping. The following errors indicate that the tracking has been affected by `Ad Blocker`. When loading through a script tag, an error may appear in the console/network tab while loading the SDK script. When loaded with npm package, there could be errors in the network tab when trying to send events to the server. The errors might vary depending on the browser.
+
+-   Chrome (Ubuntu, MacOS)  
+    Console: error net::ERR\_BLOCKED\_BY\_CLIENT  
+    Network: status (blocked:other)
+-   Firefox (Ubuntu)  
+    Console: error text doesn’t contain any blocking-specific info  
+    Network: Transferred column contains the name of plugin Blocked by uBlock Origin
+-   Safari (MacOS)  
+    Console: error contains text Content Blocker prevented frame ... from loading a resource from ...  
+    Network: it looks like blocked requests aren't listed. Not sure if it’s possible to show them.
+
+Amplitude recommends using a proxy server to avoid this situation.
+
+#### Cookies related[](#cookies-related "Permalink")
+
+Here is the [information](#cookie-management) SDK stored in the cookies. This means that client behavior, like disabling cookies or using a private browser/window/tab, will affect the persistence of these saved values in the cookies. If these values aren't persistent or aren't increasing by one, that could be the reason.
+
+#### CORS[](#cors "Permalink")
+
+Cross-Origin Resource Sharing (CORS) is a security measure implemented by browsers to restrict how resources on a web page can be requested from a different domain. It might cause this issue if you used `setServerURL`.
+
+`Access to fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.`
+
+Cross-origin resource sharing (CORS) prevents a malicious site from reading another site's data without permission. The error message suggests that the server you're trying to access isn't allowing your origin to access the requested resource. This is due to the lack of the `Access-Control-Allow-Origin` header in the server's response.
+
+-   If you have control over the server, you can "Update the server's CORS policy". Add the `Access-Control-Allow-Origin` header to the server's responses. This would allow your origin to make requests. The value of `Access-Control-Allow-Origin` can be \* to allow all origins, or it can be the specific URL of your web page.
+    
+-   If you don't have control over the server, you can set up a proxy server that adds the necessary CORS headers. The web page makes requests to the proxy, which then makes requests to the actual server. The proxy adds the `Access-Control-Allow-Origin` header to the response before sending it back to the web page.
+    
+
+If you have set up an API proxy and run into configuration issues related to that on a platform you’ve selected, that’s no longer an SDK issue but an integration issue between your application and the service provider.
+
+#### Events fired but no network requests[](#events-fired-but-no-network-requests "Permalink")
+
+If you [set the logger to "Debug" level](#debugging), and see track calls in the developer console, the `track()` method has been called. If you don't see the corresponding event in Amplitude, the Amplitude Instrumentation Explorer Chrome extension, or the network request tab of the browser, the event wasn't sent to Amplitude. Events are fired and placed in the SDK's internal queue upon a successful `track()` call, but sometimes these queued events may not send successfully. This can happen when an in-progress HTTP request is cancelled. For example, if you close the browser or leave the page.
+
+There are two ways to address this issue:
+
+1.  If you use standard network requests, set the transport to `beacon` during initialization or set the transport to `beacon` upon page exit. `sendBeacon` doesn't work in this case because it sends events in the background, and doesn't return server responses like `4xx` or `5xx`. As a result, it doesn't retry on failure. `sendBeacon` sends only scheduled requests in the background. For more information, see the [sendBeacon](#use-sendbeacon) section.
+    
+2.  To make track() synchronous, [add the `await` keyword](#callback) before the call.
+    
+
+## Advanced topics[](#advanced-topics "Permalink")
+
+### Cross-domain tracking[](#cross-domain-tracking "Permalink")
+
+You can track anonymous behavior across two different domains. Amplitude identifies anonymous users by their device IDs which must be passed between the domains. To maintain the same session and ensure a continuous user journey, also pass session IDs to the other domain.
+
+## Note
+
+Starting from `v2.8.0` the SDK supports getting the device ID from the URL parameter `ampDeviceId`. The SDK configuration, for example, `init('API_KEY', { deviceId: 'custom-device-id' })` still takes precedence over the URL parameter. Previous versions of the SDK supported the `deviceId` URL parameter, this option is still supported for backward compatibility but `ampDeviceId` will take precedence if both are set. You don't need to change your code if upgrade to versions higher than `v2.8.0` but it is recommended.
+
+For example:
+
+-   Site 1: `www.example.com`
+-   Site 2: `www.example.org`
+
+Users who start on Site 1 and then navigate to Site 2 must have the device ID generated from Site 1 passed as a parameter to Site 2. Site 2 then needs to initialize the SDK with the device ID.  
+The SDK can parse the URL parameter automatically if `deviceId` is in the URL query parameters.
+
+Starting from `v2.8.0`, the SDK can automatically get session ID from the URL to keep the same session and ensure a continuous user journey.
+
+1.  From Site 1, grab the device ID from `getDeviceId()` and the session ID from `getSessionId()`.
+2.  Pass the device ID and session ID to Site 2 through a URL parameter when the user navigates. (for example: `www.example.com?ampDeviceId=device_id_from_site_1&SessionId=1716245958483`)
+3.  Initialize the Amplitude SDK on Site 2 with `init('API_KEY', null)`.
+
+If the `deviceId` and `sessionId` aren't set in `init('API_KEY', null, { deviceId: 'custom-device-id', sessionId: 1716245958483 })`, the SDK automatically falls back to using the URL parameters respectively.
+
+#### Evaluation window with ampTimestamp[](#evaluation-window-with-amptimestamp "Permalink")
+
+## Note
+
+This feature requires @amplitude/analytics-browser@2.21.1 and above.
+
+To improve security and prevent the use of stale session or device IDs, you can include an `ampTimestamp` parameter that acts as an evaluation window. The SDK only uses `ampSessionId` and `ampDeviceId` URL parameters if the `ampTimestamp` value is in the future (greater than the current time).
+
+For example:
+
+```
+www.example.com?ampDeviceId=device_id&SessionId=session_id&Timestamp=1640995500000
+```
+
+When `ampTimestamp` expires (is less than the current time), the SDK ignores the `ampSessionId` and `ampDeviceId` parameters. It falls back to generating new values or using stored values from cookies. If `ampTimestamp` isn't provided, the SDK behaves as before for backward compatibility.
+
+This feature ensures that cross-domain tracking parameters remain valid only for a limited time window. This prevents potential security issues from long-lived URLs with embedded tracking parameters.
+
+Amplitude recommends that you follow the same session ID format as the Browser SDK using `Date.now()` because the SDK checks if an event is in session every time it tracks an event. For example:
+
+```typescript
+// if session ID is set to 12345
+// https://www.example.com?ampDeviceId=my-device-id&SessionId=12345
 amplitude.init(API_KEY)
 // session ID is set to 12345 after init()
 
-amplitude.track("event")
+amplitude.track("event")
 // session ID is set back to Date.now() 
-// because the tracked "event" is not in the previous session 12345
-
-

Custom HTTP request headers

-

Use the transport configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with transport and headers properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.

-

-

Note

-Custom headers only work with fetch and xhr transports. When using the beacon transport, the browser doesn't support custom headers due to limitations of the sendBeacon API.
-

-
amplitude.init(API_KEY, {
+// because the tracked "event" is not in the previous session 12345
+```
+
+### Custom HTTP request headers[](#custom-http-request-headers "Permalink")
+
+Use the `transport` configuration option to attach custom HTTP headers to event upload requests. Instead of passing a transport name string, pass an object with `transport` and `headers` properties. This is useful for scenarios like routing requests through a proxy server that requires specific headers.
+
+## Note
+
+Custom headers only work with `fetch` and `xhr` transports. When using the `beacon` transport, the browser doesn't support custom headers due to limitations of the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon).
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'fetch',
     headers: {
@@ -3558,9 +1545,12 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

You can also use the xhr transport with custom headers:

-
amplitude.init(API_KEY, {
+```
+
+You can also use the `xhr` transport with custom headers:
+
+```ts
+amplitude.init(API_KEY, {
   transport: {
     type: 'xhr',
     headers: {
@@ -3568,20 +1558,67 @@ Custom headers only work with fetch and xhr transports
     },
   },
 });
-
-

Use sendBeacon

-

Unlike standard network requests, sendBeacon sends events in the background, even if the user closes the browser or leaves the page.

-

-

Warning

-sendBeacon sends events in the background. As a result, events dispatched by sendBeacon don't return server responses. Keep the following in mind if you use sendBeacon:

-
    -
  1. Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
  2. -
  3. Event order cannot be guaranteed, as sendBeacon may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using fetch, the SDK waits for responses before proceeding, guaranteeing event order.
- - -

Set the transport to use sendBeacon for all events

-

To send an event using sendBeacon, set the transport SDK option to 'beacon' in one of two ways

-
amplitude.init(API_KEY, 'user@amplitude.com', 
+```
+
+### Request body compression[](#request-body-compression "Permalink")
+
+The Browser SDK supports gzip compression for event upload request bodies to reduce bandwidth usage and improve upload performance. Compression is especially beneficial when sending large batches of events.
+
+#### How compression works[](#how-compression-works "Permalink")
+
+The SDK automatically compresses request bodies when:
+
+-   The payload size is 2KB or larger.
+-   The browser supports the `CompressionStream` API (available in modern browsers).
+-   The transport type is `fetch` or `xhr` (compression isn't supported with the `beacon` transport).
+
+When using Amplitude's default ingestion endpoints (`https://api2.amplitude.com`), compression is automatically enabled. When using a custom `serverUrl` (for example, a proxy server), you must explicitly enable compression by setting `enableRequestBodyCompression` to `true`.
+
+## Note
+
+The `beacon` transport doesn't support compression because the [sendBeacon API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon) doesn't allow setting custom headers, which are required for gzip compression.
+
+#### Enable compression for custom servers[](#enable-compression-for-custom-servers "Permalink")
+
+If you route events through a custom proxy server, enable compression by setting `enableRequestBodyCompression` to `true`:
+
+```ts
+amplitude.init(API_KEY, {
+  serverUrl: 'https://your-proxy.example.com/events',
+  enableRequestBodyCompression: true,
+});
+```
+
+Your proxy server must support gzip-compressed request bodies and handle the `Content-Encoding: gzip` header.
+
+#### Browser compatibility[](#browser-compatibility "Permalink")
+
+Request body compression requires the `CompressionStream` API, which is available in:
+
+-   Chrome 80+
+-   Edge 80+
+-   Safari 16.4+
+-   Firefox 113+
+
+For browsers that don't support `CompressionStream`, the SDK automatically sends uncompressed payloads.
+
+### Use sendBeacon[](#use-sendbeacon "Permalink")
+
+Unlike standard network requests, `sendBeacon` sends events in the background, even if the user closes the browser or leaves the page.
+
+## Warning
+
+`sendBeacon` sends events in the background. As a result, events dispatched by `sendBeacon` don't return server responses. Keep the following in mind if you use `sendBeacon`:
+
+1.  Requests are not retried, including failed requests with 4xx or 5xx responses, so events may be lost.
+2.  Event order cannot be guaranteed, as `sendBeacon` may send events in parallel. This can lead to some UTM properties not being set, for example for session start events. In contrast, while using `fetch`, the SDK waits for responses before proceeding, guaranteeing event order.
+
+#### Set the transport to use sendBeacon for all events[](#set-the-transport-to-use-sendbeacon-for-all-events "Permalink")
+
+To send an event using `sendBeacon`, set the transport SDK option to 'beacon' in one of two ways
+
+```ts
+amplitude.init(API_KEY, 'user@amplitude.com', 
   {
     transport: TransportType.SendBeacon,
     // To make sure the event will be scheduled right away.
@@ -3589,1139 +1626,210 @@ Custom headers only work with fetch and xhr transports
     flushQueueSize: 1,
   }
 );
-
-

Set the transport to use beacon only when exiting page

-

Amplitude recommends adding your own event listener for pagehide event.

-
window.addEventListener('pagehide',
-  () => {
+```
+
+#### Set the transport to use beacon only when exiting page[](#set-the-transport-to-use-beacon-only-when-exiting-page "Permalink")
+
+Amplitude recommends adding your own event listener for pagehide event.
+
+```ts
+window.addEventListener('pagehide',
+  () => {
     amplitude.setTransport('beacon') 
     // Sets https transport to use `sendBeacon` API
     amplitude.flush()
   },
 );
-
-

Content Security Policy (CSP)

-

If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:

-
    -
  • When using "Script Loader", add https://*.amplitude.com to script-src.
  • -
  • Add https://*.amplitude.com to connect-src.
  • -
- -

The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.

- -
    -
  • AMP: The SDK creates user session cookies with AMP prefix and the first ten digits of the API key: AMP_{first_ten_digits_API_KEY}.
  • -
  • AMP_MKTG: The SDK creates marketing campaign cookies with AMP_MKTG and the first ten digits of the API key: AMP_MKTG_{first_ten_digits_API_KEY}.
  • -
  • AMP_TEST: On initialization, the SDK creates a cookie with AMP_TEST prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You can safely delete the AMP_TEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
  • AMP_TLDTEST: On initialization, the SDK creates a cookie with AMP_TLDTEST prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on https://analytics.amplitude.com/amplitude/home the SDK first tries to find a subdomain that matches the root domain (amplitude.com) and then falls back to the full domain (analytics.amplitude.com). You can safely delete the AMP_TLDTEST prefix cookies if, for some reason, they're not successfully deleted.
  • -
- -

By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.

-

For example, if a user logs into the website on one subdomain (data.amplitude.com) where the SDK is initialized. On initialization, the SDK assigns cookies to .amplitude.com. If the user then navigates to another subdomain (analytics.amplitude.com), the login information can be seamlessly shared by shared cookies.

- -

The SDK creates two types of cookies: user session cookies and marketing campaign cookies.

-

- User session cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
optOutRequired. A flag to opt this device out of Amplitude tracking. If this flag is set, no additional information will be stored for the user
userIdUpon user log-in, if you send this value, it is stored in the cookie. Set this to uniquely identify their users (non-anonymous navigation). It is stored encoded using Base64
deviceIdA randomly generated string. It will persist unless a user clears their browser cookies and/ or is browsing in private mode. Even if a user consistently uses the same the device and browser, the device ID can still vary
sessionIdA randomly generated string for each session
lastEventTimeTime of the last event, used to determine when to expire and create a new session Id
lastEventIdId of the last event
-

-

-

-

- Marketing campaign cookies - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameDescription
utm_campaignThis identifies a specific campaign used (for example, "summer_sale")
utm_contentThis identifies what brought the user to the site and is commonly used for A/B testing (for example, "banner-link", "text-link")
utm_idAn optional parameter for tracking unique IDs or transaction IDs associated with the link.
utm_mediumThis identifies a specific campaign used (for example, "summer_sale")
utm_sourceThis identifies which website sent the traffic (for example, Google, Facebook)
utm_termThis identifies paid search terms used (for example, product+analytics)
referrerThe last page the user was on (for example, https://amplitude.com/behavioral-analytics-platform?ref=nav)
referring_domainThe domain that the user was last on (for example, https://amplitude.com)
dclidGoogle campaign manager Click Identifier
gbraidGoogle Click Identifier for iOS device from Web to App
gclidGoogle Click Identifier from URL parameters
fbclidFacebook Click Identifier from URL parameters
ko_click_idKochava Click Identifier from URL parameters
msclkidMicrosoft Click Identifier
ttclidTikTok Click Identifier
twclidTwitter Click Identifier from URL parameter
wbraidGoogle Click Identifier for iOS device from App to Web
li_fat_idLinkedIn member indirect identifier for Members for conversion tracking, retargeting, analytics
rdt_cidReddit Click Identifier
-

-

-

-

Disable cookies

-

Opt-out of using cookies by setting identityStorage to localStorage so that the SDK will use LocalStorage instead. LocalStorage is a great alternative, but because access to LocalStorage is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: www.amplitude.com vs analytics.amplitude.com).

-
amplitude.init("api-key", null, {
-  identityStorage: "localStorage",
-});
-
-

Offline mode

-

-

Autoflush when reconnecting

-Setting config.flushIntervalMillis to a small value like 1 may cause an ERR_NETWORK_CHANGED error.
-

-

Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the config.flushIntervalMillis setting.

-

To disable offline mode, add offline: amplitude.Types.OfflineDisabled to the amplitude.init() call as shown below.

-
amplitude.init(AMPLITUDE_API_KEY, {
-  offline: amplitude.Types.OfflineDisabled
-});
-
-

Marketing Attribution Tracking

-

Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about exclude referrers and exclude internal referrers. After you enable marketing attribution tracking, Amplitude generates identify events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties.

-

Tracking scenarios

-

Amplitude tracks changes in marketing attribution in two scenarios: during SDK initialization and event processing.

-
Amplitude SDK initialization (Hard page refresh)
-
    -
  • At the start of a session, the referrer isn't excluded and campaign has any change or customer first visit.
  • -
  • In the middle of the session, the referrer isn't excluded, not direct traffic, and campaign has any change.
  • -
-

Diagram of whether tracking a campaign on SDK initialization

-

To debug, you can get the referrer by typing document.referrer in your Browser console and compare it with your config.autocapture.attribution.excludeReferrers. If document.referrer is empty, then it's considered as a direct traffic. You can get the session ID under AMP_{last 10 digits of your API key} on the "Cookies" tab of the Amplitude Chrome extension and get the previous campaign stored under AMP_MKTG_{last 10 digits of your API key}.

-
Processing the event
-
    -
  • At the start of a session, the referrer isn't excluded, and campaign has any change.
  • -
-

For more information, see the scenarios outlined below that demonstrate when Amplitude does or doesn't track marketing attribution. These examples are illustrative, not exhaustive.

-

Tracking occurs when either of the following applies:

- - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The current subdomain is not an excluded referrer.The referrer does not originates from the same domain or the current subdomain is not match any referrer in config.autocapture.attribution.excludeReferrers.
No previous campaign.A user's initial visit.
There is an introduction of new UTM parameter or Click ID parameter.If any utm parameters or Click ID parameters have been dropped during a session, we will unset it.
The referrer domain changes to a new one.Referrer domain changed from a.test.com to b.test-new.com
-

Amplitude doesn't track marketing attribution under any of the following conditions:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RuleExample
The referrer originates from the same domain with default configuration.The landing page is a.test.com, with the referrer set to b.test.com.
A specific referrer domain is explicitly excluded.When setting config.autocapture.attribution.excludeReferrers = [a.test.com], and the referrer domain is a.test.com for the current page.
The subdomain is specified or matches the regular expression in config.autocapture.attribution.excludeReferrers.Configuration of excludeReferrers involves specific string arrays or a regular expression.
The user engages in direct traffic within the same session.During a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
SPA redirect without page reloadingDuring a session, a user clicks on a link without any campaign attribution parameters, including the absence of UTM and click id parameters from an email.
-

Rogue referral problem for SPAs

-

SPA typically don't experience a true page load after a visitor enters the site, which means the referrer information doesn't update when clicking internal links. UTM parameters may be dropped during SPA redirects, while the referrer remains unchanged. This is a known issue in the industry. To address this problem, you can either:

-
    -
  • Control the page and location parameters and / or
  • -
  • Unset the referrer after the first hit
  • -
-

Remote configuration

-

Beginning with version 2.10.0, the Amplitude Browser SDK supports remote configuration.

-

-

Default behavior changed in version 2.16.1

-Starting in SDK version 2.16.1, fetchRemoteConfig is enabled by default (true). For versions 2.10.0 to 2.16.0, remote configuration was disabled by default and required explicit enablement.
-

-

Autocapture supports remote configuration options for tracking default events. When remote configuration is enabled, settings from Amplitude's servers merge with your local SDK configuration, with remote settings taking precedence. Find the remote configuration options in Data > Settings > Autocapture.

-

Enable or disable remote configuration

-

For SDK versions 2.16.1 and later: Remote configuration is enabled by default. To disable it, explicitly set fetchRemoteConfig: false:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: false  // Disable remote config
-});
-
-

For SDK versions 2.10.0 to 2.16.0: Remote configuration is disabled by default. To enable it, set fetchRemoteConfig: true:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  fetchRemoteConfig: true  // Enable remote config (only needed for versions < 2.16.1)
-});
-
-

-

Configuration merging behavior

-When you enable fetchRemoteConfig, the SDK merges remote configuration with local configuration at the feature level. Remote configuration can override specific autocapture features even when you set autocapture: false locally.

-

How the merging works:

-
    -
  • If remote configuration specifies a value for an autocapture feature, that value takes precedence.
  • -
  • If remote configuration doesn't specify a value for a feature, the SDK uses the local configuration value.
  • -
  • Each autocapture feature such as sessions, pageViews, and elementInteractions merges independently.
  • -
-

- Remote config enables specific features when local config disables all - -

-
// Local configuration disables all autocapture
-amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: false
+```
+
+### Content Security Policy (CSP)[](#content-security-policy-csp "Permalink")
+
+If your web app configures the strict Content Security Policy (CSP) for security concerns, adjust the policy to whitelist the Amplitude domains:
+
+-   When using ["Script Loader"](https://github.com/amplitude/Amplitude-TypeScript/tree/main/packages/analytics-browser#installing-via-script-loader), add `https://*.amplitude.com` to `script-src`.
+-   Add `https://*.amplitude.com` to `connect-src`.
+
+### Cookie management[](#cookie-management "Permalink")
+
+The Browser SDK uses cookie storage to persist information that multiple subdomains of the same domain may likely want to share. This includes information like user sessions and marketing campaigns, which are stored in separate cookie entries.
+
+#### Cookie prefix[](#cookie-prefix "Permalink")
+
+-   **AMP**: The SDK creates user session cookies with `AMP` prefix and the first ten digits of the API key: `AMP_{first_ten_digits_API_KEY}`.
+-   **AMP\_MKTG**: The SDK creates marketing campaign cookies with `AMP_MKTG` and the first ten digits of the API key: `AMP_MKTG_{first_ten_digits_API_KEY}`.
+-   **AMP\_TEST**: On initialization, the SDK creates a cookie with `AMP_TEST` prefix to check whether the cookie storage is working properly. Then the SDK sets the value as the current time, retrieves the cookie by a key and checks if the retrieved value matches the original set time. You **can safely delete** the `AMP_TEST` prefix cookies if, for some reason, they're not successfully deleted.
+-   **AMP\_TLDTEST**: On initialization, the SDK creates a cookie with `AMP_TLDTEST` prefix to find a subdomain that supports cookie storage. For example, when checking for cookie support on `https://analytics.amplitude.com/amplitude/home` the SDK first tries to find a subdomain that matches the root domain (`amplitude.com`) and then falls back to the full domain (`analytics.amplitude.com`). You **can safely delete** the `AMP_TLDTEST` prefix cookies if, for some reason, they're not successfully deleted.
+
+#### Cookie domain[](#cookie-domain "Permalink")
+
+By default, the SDK assigns these cookies to the top-level domain which supports cookie storage. Cookies can be shared on multiple subdomains which allows for a seamless user experience across all subdomains.
+
+For example, if a user logs into the website on one subdomain (`data.amplitude.com`) where the SDK is initialized. On initialization, the SDK assigns cookies to `.amplitude.com`. If the user then navigates to another subdomain (`analytics.amplitude.com`), the login information can be seamlessly shared by shared cookies.
+
+#### Cookie data[](#cookie-data "Permalink")
+
+The SDK creates two types of cookies: user session cookies and marketing campaign cookies.
+
+#### Disable cookies[](#disable-cookies "Permalink")
+
+Opt-out of using cookies by setting `identityStorage` to `localStorage` so that the SDK will use `LocalStorage` instead. `LocalStorage` is a great alternative, but because access to `LocalStorage` is restricted by subdomain, you can't track anonymous users across subdomains of your product (for example: `www.amplitude.com` vs `analytics.amplitude.com`).
+
+```ts
+amplitude.init("api-key", null, {
+  identityStorage: "localStorage",
 });
-

// Remote config (set in Data > Settings > Autocapture) enables only: -// - Page Views: enabled -// - Element Interactions: enabled

-

// Result: Only Page Views and Element Interactions are tracked -// All other features (sessions, formInteractions, fileDownloads, etc.) remain disabled -

-

-

-

-

- Remote config overrides specific local settings - -

-
// Local configuration enables most features
+```
+
+### Offline mode[](#offline-mode "Permalink")
+
+## Autoflush when reconnecting
+
+Setting `config.flushIntervalMillis` to a small value like `1` may cause an `ERR_NETWORK_CHANGED` error.
+
+Beginning with version 2.4.0, the Amplitude Browser SDK supports offline mode. The SDK checks network connectivity every time it tracks an event. If the device is connected to network, the SDK schedules a flush. If not, it saves the event to storage. The SDK also listens for changes in network connectivity and schedules a flush of all stored events when the device reconnects, based on the `config.flushIntervalMillis` setting.
+
+To disable offline mode, add `offline: amplitude.Types.OfflineDisabled` to the `amplitude.init()` call as shown below.
+
+```ts
 amplitude.init(AMPLITUDE_API_KEY, {
-  autocapture: {
-    pageViews: true,
-    sessions: true,
-    elementInteractions: true,
-    formInteractions: true
-  }
-});
-

// Remote config (set in Data > Settings > Autocapture): -// - Element Interactions: disabled -// - Frustration Interactions: enabled

-

// Result: -// - pageViews: true (from local config) -// - sessions: true (from local config) -// - elementInteractions: false (overridden by remote config) -// - formInteractions: true (from local config) -// - frustrationInteractions: true (set by remote config) -

-

-

-

-

- Remote config when you don't specify local config - -

-
// Local configuration doesn't specify autocapture settings
-amplitude.init(AMPLITUDE_API_KEY);
-

// Remote config (set in Data > Settings > Autocapture): -// - Page Views: enabled -// - Sessions: enabled

-

// Result: Only Page Views and Sessions are tracked -// All other features use their default values -

-

-

-

-

Set baseline settings locally and adjust specific features remotely through the Amplitude UI without code changes.

-

-

In Amplitude, navigate to Data > Settings > Autocapture to add or update a remote configuration.

-

Proxy remote config requests

-

To proxy remote configuration requests through your own server (for example, to bypass ad blockers), configure the remoteConfig option:

-
amplitude.init(AMPLITUDE_API_KEY, {
-  remoteConfig: {
-    serverUrl: 'https://your-proxy.example.com/config'
-  }
+  offline: amplitude.Types.OfflineDisabled
 });
-
-

When remoteConfig.serverUrl is set, the SDK sends remote configuration requests to your custom URL instead of Amplitude's endpoints. Analytics events still use serverUrl or the default Amplitude endpoints.

-

-

Note

-The top-level fetchRemoteConfig option is deprecated. Use remoteConfig.fetchRemoteConfig instead for new implementations.
-

+``` - -
-
+### Marketing Attribution Tracking[](#marketing-attribution-tracking "Permalink") - - +Amplitude tracks marketing attribution and excludes all referrers from subdomains by default. Learn more about [exclude referrers](#exclude-referrers) and [exclude internal referrers](#exclude-internal-referrers). After you enable marketing attribution tracking, Amplitude generates `identify` events to assign the campaign values as user properties in specific scenarios. Refer to the following section to learn when Amplitude tracks marketing attribution and updates user properties. - +A user's initial visit. - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

June 12th, 2025

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - +If any utm parameters or Click ID parameters have been dropped during a session, we will unset it. - - - - - - - - - - +## Note - - \ No newline at end of file +The top-level `fetchRemoteConfig` option is deprecated. Use `remoteConfig.fetchRemoteConfig` instead for new implementations. \ No newline at end of file diff --git a/skills/integration/integration-vue-3/references/browser-unified-sdk.md b/skills/integration/integration-vue-3/references/browser-unified-sdk.md index 516aa8f66..145ce2da3 100644 --- a/skills/integration/integration-vue-3/references/browser-unified-sdk.md +++ b/skills/integration/integration-vue-3/references/browser-unified-sdk.md @@ -1,1321 +1,151 @@ - +Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs. - - - - - - +## Tip - - - - - - - - - Browser Unified SDK | Amplitude - - - - - - - - - - - - - - - - - - +To skip manual setup, use the [Amplitude Setup Wizard CLI](/docs/get-started/setup-wizard-cli). It reads your codebase, proposes tracking events, and instruments the SDK automatically with your approval. - - - - - - - -
-
-
-
- - - - -
-
- -
-
-
-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - SDKs - - APIs - -
-
- - - - - - - - - - -
- - -
-
- -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-
-
- -
-
-
-

Browser Unified SDK

-
-
- - - - - - - - -

Amplitude offers multiple ways to install browser SDKs, each with different product support and version control options. This guide explains the three main installation methods and helps you choose the right approach for your needs.

-

Choose your installation method

-

Amplitude provides three main ways to install browser SDKs:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MethodDescriptionVersion controlBest for
Unified SDK (npm)Single npm package with all Amplitude featuresCustomer-managedTeams requiring reproducible builds and strict change management
Unified Script (CDN)Single script tag that loads Amplitude capabilitiesAmplitude-managedQuick setup with automatic updates and sensible defaults
GTM TemplateGoogle Tag Manager templateTemplate version-controlledTeams using GTM for tag management
-

Product support by installation method

-

Different installation methods support different Amplitude products:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ProductUnified Script (CDN)Unified SDK (npm)GTM Template
Analytics (@amplitude/analytics-browser)✅ Included✅ Included✅ Included
Session Replay✅ Included✅ Included✅ Optional (checkbox)
Guides & Surveys⚠️ Separate script✅ Included✅ Optional (checkbox)
Web Experiment (@amplitude/experiment-tag)✅ Included❌ Not included❌ Not supported
Feature Experiment (@amplitude/experiment-js-client)❌ Not included✅ Included❌ Not supported
-

-

Web Experiment compared with Feature Experiment

-
    -
  • Web Experiment: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
  • -
  • Feature Experiment: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
- - -

Unified SDK (npm)

-

The Unified SDK provides a single npm package (@amplitude/unified) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your package.json.

-

Key characteristics:

-
    -
  • Customer-managed package installation with full control over versions.
  • -
  • Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
  • -
  • Upgrade by bumping the package version in your dependency file.
  • -
  • Ideal for teams that require reproducible builds and strict change management.
  • -
-

-

Individual product installation

-If you're concerned about bundle size and only need specific products, install them individually:

-
- - -

Install the Unified SDK

-

Install the dependency with npm or yarn.

-

-

-
-
- -

-
npm install @amplitude/unified
-
-

-

-

-
yarn add @amplitude/unified
-
-

-

-
-

Paste the prompt below into your terminal or AI tools like Copilot, Cursor, Replit, or Bolt.

-

Click the Key icon to insert your Amplitude API key. -

-
You are an Amplitude installation wizard, an expert AI programming assistant that implements Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for JavaScript-based applications.
-

Your task is to select, install, and initialize the correct Amplitude package(s) necessary to enable Amplitude Analytics, Session Replay, Experiment, and Guides and Surveys for this application and track key interactions, all in strict accordance to the Documentation provided below.

-

Rules

-
    -
  • Do not make any code changes if this is not a JavaScript-based application
  • -
  • Ensure ALL the code added ONLY runs client-side and never server-side
  • -
  • Ensure amplitude is only initialized once during the lifecycle of the application
  • -
-

Context

-

Documentation

-
    -
  1. Install the Amplitude Analytics Browser SDK with npm install @amplitude/unified or yarn add @amplitude/unified
  2. -
  3. Import amplitude into the root of the client application with import * as amplitude from '@amplitude/unified';
  4. -
  5. Initialize amplitude with amplitude.initAll('AMPLITUDE_API_KEY', {"analytics":{"autocapture":true},"sessionReplay":{"sampleRate":1},"experiment":{"deploymentKey":"DEPLOYMENT_KEY"},"engagement":{"serverZone":"US","logLevel":LogLevel.Warn}});
- -

-

-

-

Initialize the Unified SDK

-

The Unified SDK provides a single initialization method that initializes all Amplitude features.

-
import { initAll } from '@amplitude/unified';
+## Choose your installation method[](#choose-your-installation-method "Permalink")
+
+Amplitude provides three main ways to install browser SDKs:
+
+Method
+
+Description
+
+Version control
+
+Best for
+
+[Unified SDK (npm)](#unified-sdk-npm)
+
+Single npm package with all Amplitude features
+
+Customer-managed
+
+Teams requiring reproducible builds and strict change management
+
+[Unified Script (CDN)](#unified-script-cdn)
+
+Single script tag that loads Amplitude capabilities
+
+Amplitude-managed
+
+Quick setup with automatic updates and sensible defaults
+
+[GTM Template](#google-tag-manager)
+
+Google Tag Manager template
+
+Template version-controlled
+
+Teams using GTM for tag management
+
+### Product support by installation method[](#product-support-by-installation-method "Permalink")
+
+Different installation methods support different Amplitude products:
+
+Product
+
+Unified Script (CDN)
+
+Unified SDK (npm)
+
+GTM Template
+
+Analytics (`@amplitude/analytics-browser`)
+
+✅ Included
+
+✅ Included
+
+✅ Included
+
+Session Replay
+
+✅ Included
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Guides & Surveys
+
+⚠️ Separate script
+
+✅ Included
+
+✅ Optional (checkbox)
+
+Web Experiment (`@amplitude/experiment-tag`)
+
+✅ Included
+
+❌ Not included
+
+❌ Not supported
+
+Feature Experiment (`@amplitude/experiment-js-client`)
+
+❌ Not included
+
+✅ Included
+
+❌ Not supported
+
+## Web Experiment compared with Feature Experiment
+
+-   **Web Experiment**: Uses visual editing for no-code A/B testing on web pages. The Unified Script includes it automatically.
+-   **Feature Experiment**: Uses code-based feature flags with the Experiment JavaScript SDK. The Unified npm package includes it.
+
+## Unified SDK (npm)[](#unified-sdk-npm "Permalink")
+
+The Unified SDK provides a single npm package (`@amplitude/unified`) giving you access to Analytics, Session Replay, Feature Experiment, and Guides & Surveys through a single API. Install it with npm or yarn, and control the version in your `package.json`.
+
+**Key characteristics:**
+
+-   Customer-managed package installation with full control over versions.
+-   Includes Feature Experiment (code-based feature flags), not Web Experiment (visual editor).
+-   Upgrade by bumping the package version in your dependency file.
+-   Ideal for teams that require reproducible builds and strict change management.
+
+## Individual product installation
+
+If you're concerned about bundle size and only need specific products, install them individually:
+
+-   [Analytics](/docs/sdks/analytics/browser/browser-sdk-2): For tracking user events and behavior.
+-   [Experiment](/docs/sdks/experiment-sdks/experiment-javascript): For running A/B tests and feature flags.
+-   [Session Replay](/docs/session-replay/session-replay-standalone-sdk): For capturing and replaying user sessions.
+-   [Guides and Surveys](/docs/guides-and-surveys/sdk): For in-product messaging and surveys.
+
+### Install the Unified SDK[](#install-the-unified-sdk "Permalink")
+
+Install the dependency with npm or yarn.
+
+### Initialize the Unified SDK[](#initialize-the-unified-sdk "Permalink")
+
+The Unified SDK provides a single initialization method that initializes all Amplitude features.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY');
-
-

Access SDK features

-

The Unified SDK provides access to all Amplitude features through a single interface:

-

-

Feature Documentation

-For detailed information about each product's features and APIs, refer to their respective documentation:

-
- - -
import { 
+```
+
+### Access SDK features[](#access-sdk-features "Permalink")
+
+The Unified SDK provides access to all Amplitude features through a single interface:
+
+## Feature Documentation
+
+For detailed information about each product's features and APIs, refer to their respective documentation:
+
+-   [Analytics Browser SDK](/docs/sdks/analytics/browser/browser-sdk-2)
+-   [Experiment JavaScript SDK](/docs/sdks/experiment-sdks/experiment-javascript)
+-   [Session Replay Standalone SDK](/docs/session-replay/session-replay-standalone-sdk)
+-   [Guides and Surveys Web SDK](/docs/guides-and-surveys/sdk)
+
+```typescript
+import { 
   track, 
   identify, 
   experiment, 
@@ -1333,10 +163,14 @@ const variant = await experiment.fetch('experiment-key');
 
 // Access Session Replay features
 sessionReplay.flush();
-
-

Configuration

-

The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.

-
import { initAll } from '@amplitude/unified';
+```
+
+### Configuration[](#configuration "Permalink")
+
+The Unified SDK supports configuration options for all Amplitude features. You can configure each product individually while sharing some common options.
+
+```typescript
+import { initAll } from '@amplitude/unified';
 
 initAll('AMPLITUDE_API_KEY', {
   // Shared options for all SDKs (optional)
@@ -1364,59 +198,80 @@ initAll('AMPLITUDE_API_KEY', {
     // Guides and Surveys configuration options
   }
 });
-
-

Shared options

- - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDefaultDescription
serverZone'US' or 'EU''US'The server zone to use for all SDKs.
instanceNamestring$default_instanceA unique name for this instance of the SDK.
-

Analytics options

-

All options from @amplitude/analytics-browser are supported. Refer to the Analytics Browser SDK documentation for details.

-

Session Replay options

-

The Unified Browser SDK supports all options from @amplitude/plugin-session-replay-browser. Refer to the Session Replay Plugin documentation for more information. Set config.sessionReplay.sampleRate to a non-zero value to enable Session Replay.

-

Sample rate controls the rate at which Amplitude captures session replays. For example, if you set config.sessionReplay.sampleRate to 0.5, Session Replay captures roughly half of all sessions.

-

Experiment options

-

All options from @amplitude/plugin-experiment-browser are supported. Refer to the Experiment documentation for details.

-

Guides and Surveys options

-

The Unified Browser SDK supports all Guides and Surveys options. The engagement plugin initializes automatically when you pass engagement options in the configuration.

-

Unified script (CDN)

-

The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.

-

Key characteristics:

-
    -
  • Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
  • -
  • Includes Web Experiment (visual editor) by default.
  • -
  • Session Replay and Web Experiment enable automatically with default settings.
  • -
  • Guides & Surveys requires a separate script because of size concerns.
  • -
-

Install the Unified Script

-

Add the following script tag to the <head> of your site:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-
-

Replace AMPLITUDE_API_KEY with your project's API key.

-

Initialize the Unified Script

-

The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:

-
<script src="https://cdn.amplitude.com/script/AMPLITUDE_API_KEY.js"></script>
-<script>
+```
+
+#### Shared options[](#shared-options "Permalink")
+
+Name
+
+Type
+
+Default
+
+Description
+
+`serverZone`
+
+`'US'` or `'EU'`
+
+`'US'`
+
+The server zone to use for all SDKs.
+
+`instanceName`
+
+`string`
+
+`$default_instance`
+
+A unique name for this instance of the SDK.
+
+#### Analytics options[](#analytics-options "Permalink")
+
+All options from `@amplitude/analytics-browser` are supported. Refer to the [Analytics Browser SDK documentation](/docs/sdks/analytics/browser/browser-sdk-2#initialize-the-sdk) for details.
+
+#### Session Replay options[](#session-replay-options "Permalink")
+
+The Unified Browser SDK supports all options from `@amplitude/plugin-session-replay-browser`. Refer to the [Session Replay Plugin documentation](/docs/session-replay/session-replay-plugin#configuration) for more information. Set `config.sessionReplay.sampleRate` to a non-zero value to enable Session Replay.
+
+Sample rate controls the rate at which Amplitude captures session replays. For example, if you set `config.sessionReplay.sampleRate` to `0.5`, Session Replay captures roughly half of all sessions.
+
+#### Experiment options[](#experiment-options "Permalink")
+
+All options from `@amplitude/plugin-experiment-browser` are supported. Refer to the [Experiment documentation](/docs/sdks/experiment-sdks/experiment-javascript#configuration) for details.
+
+#### Guides and Surveys options[](#guides-and-surveys-options "Permalink")
+
+The Unified Browser SDK supports all [Guides and Surveys options](/docs/guides-and-surveys/sdk#initialize-the-sdk). The engagement plugin initializes automatically when you pass engagement options in the configuration.
+
+## Unified script (CDN)[](#unified-script-cdn "Permalink")
+
+The Unified Script is a single script tag that loads Amplitude browser capabilities from Amplitude's CDN. Amplitude remotely controls the versions of the underlying SDKs, offering a "single line of code" experience with sensible defaults and optional remote or manual configuration.
+
+**Key characteristics:**
+
+-   Amplitude manages SDK versions and can patch bugs or improve performance centrally without requiring a customer release.
+-   Includes Web Experiment (visual editor) by default.
+-   Session Replay and Web Experiment enable automatically with default settings.
+-   Guides & Surveys requires a separate script because of size concerns.
+
+### Install the Unified Script[](#install-the-unified-script "Permalink")
+
+Add the following script tag to the `` of your site:
+
+```html
+
+```
+
+Replace `AMPLITUDE_API_KEY` with your project's API key.
+
+### Initialize the Unified Script[](#initialize-the-unified-script "Permalink")
+
+The Unified Script enables Session Replay and Web Experiment by default. For manual configuration:
+
+```html
+
+
+```
 
+### Add Guides & Surveys to the Unified Script[](#add-guides--surveys-to-the-unified-script "Permalink")
 
-    
-
- - - - -
-
-
- - - - - - - -

- Package Information -

-
-
-
-
Package Name
- @amplitude/unified -
-
-
-
Version
-
-
-
-
-
-
Size (gzip)
-
-
-
-
-
-
-
-
- - +Because of size concerns, Guides & Surveys requires a separate script. Add it after the Unified Script: +```html + + +``` +Refer to the [Guides and Surveys SDK documentation](/docs/guides-and-surveys/sdk) for more configuration options. +## Google Tag Manager[](#google-tag-manager "Permalink") +The [Amplitude GTM template](/docs/data/source-catalog/google-tag-manager) directly wraps the Analytics Browser 2.0 SDK (`@amplitude/analytics-browser`). It doesn't use the Unified SDK or Unified Script. +**Key characteristics:** -
-
+- Template version-controlled through GTM. +- Supports Analytics, Session Replay, and Guides & Surveys. +- Doesn't support Web Experiment or Feature Experiment. +- Session Replay and Guides & Surveys are disabled by default and require manual checkbox selection. -
+### Enable Session Replay and Guides & Surveys in GTM[](#enable-session-replay-and-guides--surveys-in-gtm "Permalink") - -
-
- -
-
- Was this page helpful? -
- -
-
- - -

April 15th, 2026

-
- -
-
-

Need help? Contact Support

- -

Have a look at the Amplitude Blog

-

Learn more at Amplitude Academy

- -
-
-
-
-
- - - - - - - - - - - - - -
-
- -

© 2026 Amplitude, Inc. All rights reserved. Amplitude is a registered trademark of Amplitude, Inc.

-
-
-
- -
-
- - - - - - - - - - - - - - - +1. Navigate to your Amplitude tag in GTM. +2. In the tag configuration, check the **Session Replay** checkbox to enable session replays. +3. Check the **Guides & Surveys** checkbox to enable in-product messaging. +4. Save and publish your changes. - - \ No newline at end of file +Refer to the [Google Tag Manager documentation](/docs/data/source-catalog/google-tag-manager) for detailed configuration options. \ No newline at end of file diff --git a/skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md b/skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md index 459eed4a2..1bdda7965 100644 --- a/skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md +++ b/skills/taxonomy/amplitude-quickstart-taxonomy-agent/SKILL.md @@ -113,8 +113,26 @@ Infer which Amplitude patterns apply: 1. **Business outcomes, not UI noise** — what happened, not “button clicked”. Prefer `Order Completed`, `Trial Started`, not `Modal Opened`. -2. **Autocapture-first** — Do **not** define custom “Page Viewed” (Autocapture - covers pages). Focus on state changes and meaningful actions. +2. **Autocapture-first** — Do **not** emit custom events that duplicate anything Amplitude Browser SDK Autocapture already produces. Suggest custom events **only** for business outcomes and state changes Autocapture can’t see. + + **Excluded event names** (emitted by Autocapture — never suggest these as custom events): + + | Autocapture option | Event name(s) | + | ------------------------- | ------------------------------------------------------------------------------------------------------------ | + | `pageViews` | `[Amplitude] Page Viewed` | + | `sessions` | `[Amplitude] Start Session`, `[Amplitude] End Session` | + | `formInteractions` | `[Amplitude] Form Started`, `[Amplitude] Form Submitted` | + | `fileDownloads` | `[Amplitude] File Downloaded` | + | `elementInteractions` | `[Amplitude] Element Clicked`, `[Amplitude] Element Changed` | + | `frustrationInteractions` | `[Amplitude] Rage Click`, `[Amplitude] Dead Click`, `[Amplitude] Error Click`, `[Amplitude] Thrashed Cursor` | + | `networkTracking` | `[Amplitude] Network Request` | + | `webVitals` | `[Amplitude] Web Vitals` | + + **Also exclude conceptual duplicates** regardless of name. Examples: `Page Viewed`, `Button Clicked`, `Link Clicked`, `Form Submitted`, `Session Started`, `File Downloaded` — these all duplicate Autocapture in concept even without the `[Amplitude]` prefix. Prefer business outcomes (`Order Completed`, `Signup Completed`, `Video Watched`) over UI interactions. + + Attribution does **not** emit an event — it sets user properties — so no exclusion is needed there. + + *Source of truth:* this list is maintained here. The sibling instrumentation skills (`skills/instrumentation/instrument-events/references/best-practices.md` and `skills/instrumentation/discover-event-surfaces/references/best-practices.md`) carry an abbreviated version and must be updated in lockstep when the authoritative list changes. 3. **Properties (max ~7 per event)** — factual, stable, chart-useful. **No PII.** - For transactional events: **`product_engagement.*`** + @@ -128,11 +146,15 @@ Infer which Amplitude patterns apply: - Transactional events: dual arrays where applicable - Funnel events: shared properties for linkage -- No custom generic pageview events +- **No suggested event duplicates an Autocapture-covered event** — verify against + the excluded-event table in Step 6, rule 2 (includes `[Amplitude] Page Viewed`, + element / form / session / file-download / frustration / network / web-vitals + events, and conceptual equivalents like `Button Clicked`) - Count discipline: 10–30 events - Error coverage: `Error Encountered` if flows can fail -- Naming: Lowercase with spaces for event names (e.g. `order completed`), - consistent property naming +- Naming: Title Case with spaces following `[Noun] + [Past-Tense Verb]` (e.g. + `Order Completed`, `Video Watched`). Property names use snake_case. Apply + consistently across all events. --- diff --git a/src/commands/default.ts b/src/commands/default.ts index fe196ef22..c4f9acbc9 100644 --- a/src/commands/default.ts +++ b/src/commands/default.ts @@ -593,8 +593,6 @@ export const defaultCommand: CommandModule = { // Apply SDK-level opt-out based on feature flags analytics.applyOptOut(); - const { FRAMEWORK_REGISTRY } = await import('../lib/registry.js'); - const { detectAllFrameworks } = await import('../run.js'); const installDir = session.installDir ?? process.cwd(); // Verbose startup diagnostics — always written to the log file; @@ -612,8 +610,6 @@ export const defaultCommand: CommandModule = { logToFile(`[verbose] argv : ${process.argv.join(' ')}`); } - const { DETECTION_TIMEOUT_MS } = await import('../lib/constants.js'); - // ── OAuth + account setup ────────────────────────────── // Runs concurrently with framework detection while AuthScreen shows. // When OAuth completes, store.setOAuthComplete() triggers the @@ -1092,87 +1088,38 @@ export const defaultCommand: CommandModule = { })(); // ── Framework detection ──────────────────────────────── - // Runs concurrently with auth while AuthScreen shows. - // Each detector has its own per-framework timeout internally, - // so no outer timeout is needed. - const detectionTask = (async () => { - const results = await detectAllFrameworks(installDir); - - // Store full results on session for diagnostics - session.detectionResults = results; - - const detectedIntegration = results.find( - (r) => r.detected, - )?.integration; - - if (detectedIntegration) { - const config = FRAMEWORK_REGISTRY[detectedIntegration]; - - // Run gatherContext for the friendly variant label - if (config.metadata.gatherContext) { - try { - const context = await Promise.race([ - config.metadata.gatherContext({ - installDir, - debug: session.debug, - forceInstall: session.forceInstall, - default: false, - signup: session.signup, - localMcp: session.localMcp, - ci: session.ci, - menu: session.menu, - benchmark: session.benchmark, - }), - new Promise>((resolve) => - setTimeout(() => resolve({}), DETECTION_TIMEOUT_MS), - ), - ]); - for (const [key, value] of Object.entries(context)) { - if (!(key in session.frameworkContext)) { - tui.store.setFrameworkContext(key, value); - } - } - } catch { - // Detection failed — will show generic name - } - } - - tui.store.setFrameworkConfig(detectedIntegration, config); - - if (!session.detectedFrameworkLabel) { - tui.store.setDetectedFramework(config.metadata.name); - } - } + // Runs concurrently with auth while AuthScreen shows. Each + // detector has its own per-framework timeout internally, so + // no outer timeout is needed. The shared `runFrameworkDetection` + // helper handles detect → gatherContext → setFrameworkConfig → + // discoverFeatures → autoEnableInlineAddons → setDetectionComplete + // in the right order, including the abort-aware short-circuits + // that let a directory change mid-detection cancel the + // in-flight run cleanly. + const { runFrameworkDetection } = await import( + '../lib/framework-detection.js' + ); - // Feature discovery — same helper that CI/agent uses, so the - // package and integration lists never drift between modes. - const { discoverFeatures } = await import( - '../lib/feature-discovery.js' - ); - const runDiscovery = () => { - for (const f of discoverFeatures({ - installDir, - integration: tui.store.session.integration, - })) { - tui.store.addDiscoveredFeature(f); - } - }; - runDiscovery(); - - // Re-run when integration changes (handles manual selection - // after auto-detection fails). Track last-seen to avoid the - // package.json scan firing on every store emit. - let lastIntegration = tui.store.session.integration; - tui.store.subscribe(() => { - const integration = tui.store.session.integration; - if (integration === lastIntegration) return; - lastIntegration = integration; - runDiscovery(); - }); + // 1) Wire the redetector so the IntroScreen's "Change directory" + // flow can re-run detection against a new tree. Without + // this, `store.changeInstallDir(newDir)` resets + // `detectionComplete=false` but the spinner never advances — + // it sat there forever before this hookup. + tui.store.setFrameworkRedetector((newDir, signal) => + runFrameworkDetection(tui.store, newDir, { signal }), + ); - // Signal detection is done — IntroScreen shows picker or results - tui.store.setDetectionComplete(); - })(); + // 2) Run the INITIAL detection with an abort controller the + // store can reach. If the user picks "Change directory" + // while this first run is still scanning, the store cancels + // it through this controller so a stale + // `setDetectionComplete()` can't fire after the directory + // swap. + const detectionController = new AbortController(); + tui.store.registerActiveDetection(detectionController); + const detectionTask = runFrameworkDetection(tui.store, installDir, { + signal: detectionController.signal, + }); // Gate runWizard on the user reaching RunScreen — at that point // auth, data check, and any setup questions are all complete.