diff --git a/results/source-maps-sol-medium/README.md b/results/source-maps-sol-medium/README.md new file mode 100644 index 000000000..409233296 --- /dev/null +++ b/results/source-maps-sol-medium/README.md @@ -0,0 +1,39 @@ +# Source-maps sol-medium binding — verification runs + +Each app ran the `error-tracking-upload-source-maps` program twice against project +**228144**: once on the new binding (`pi` + `gpt-5.6-sol`, `thinkingLevel: medium`) +and once on the prior default (`anthropic` + `claude-sonnet-4-6`). `SOURCE_MAPS_RUN_BUILD=1`, +so the host ran each app's real `npm run build` at the skill's test step — exercising the +actual `sourcemap process` upload. One directory per run: `app.diff` (what the agent +changed), `ctrl.log` (screen path), `result.json`. + +## Uploads that landed + +33 symbol sets in [project 228144](https://us.posthog.com/project/228144/error_tracking/configuration) +over the runs. Both bindings produce a working upload when the agent (a) writes the vaulted +upload key and (b) installs the build deps. + +## Per-app outcome + +| app | sol-medium (pi/gpt-5.6-sol) | anthropic (claude-sonnet-4-6) | +|-----|------------------------------|-------------------------------| +| `next` | **0 uploaded** — kept the fixture's stale `POSTHOG_API_KEY` | **29 chunks** — replaced the stale key with the vaulted one | +| `react-vite` | **1 chunk** — identical `vite.config.ts` | **1 chunk** — identical `vite.config.ts` | +| `node-raw` | **1 chunk** — ran `npm install`, build compiled | **0 uploaded** — never installed deps, `tsc: command not found` | + +## Read + +The integration logic is **1:1 across bindings** — both write the same env keys, the same +build-script/`withPostHogConfig`/`rollup-plugin` wiring, the same task plan (it's +skill-driven). Where they differ is **execution thoroughness**, and it cuts both ways: + +- `next`: sol-medium trusted a stale `POSTHOG_API_KEY` already in the fixture's `.env.local`; + anthropic overwrote it. (Anthropic better.) +- `node-raw`: sol-medium ran `npm install` first so `tsc` was on PATH; anthropic skipped it + and the build couldn't compile. (Sol-medium better.) +- `react-vite`: no meaningful difference — the recipe left no room for judgment. + +Net: the binding routes correctly and the upload works under both. The divergences are +run-to-run agent variance on edge cases (stale creds, missing deps), not a systematic +difference between `sol-medium` and `sonnet-4-6` — a 1-run sample per cell can't separate +model signal from noise. diff --git a/results/source-maps-sol-medium/next__anthropic/app.diff b/results/source-maps-sol-medium/next__anthropic/app.diff new file mode 100644 index 000000000..01df19660 --- /dev/null +++ b/results/source-maps-sol-medium/next__anthropic/app.diff @@ -0,0 +1,1420 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md +index 897a35c..6a58547 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md +@@ -3,7 +3,7 @@ name: error-tracking-upload-source-maps-nextjs + description: Upload source maps to PostHog Error Tracking for Next.js + metadata: + author: PostHog +- version: 1.37.1 ++ version: 1.49.1 + --- + + # Upload source maps to PostHog for Next.js +@@ -17,7 +17,7 @@ This skill helps you upload source maps (or platform debug symbols) so PostHog E + - `references/cli.md` - Upload source maps with cli - docs + - `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow + +-The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. + + ## Steps + +@@ -59,8 +59,22 @@ Wire source map generation, chunk-ID injection, and upload into your **productio + 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. + 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. + 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. + - **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. +-- **React Native** You upload platform debug symbols (Hermes maps, dSYMs) rather than plain `.js.map` files — follow the platform reference for the exact build hook. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. + - **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. + - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. + - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. +@@ -80,10 +94,12 @@ The upload credentials must be readable **by the build pipeline at build time**, + - **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) + - **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. + - **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. + - **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: + - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). + - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. + - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. + + #### Examples + - **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. +@@ -122,6 +138,8 @@ The upload credentials must be readable **by the build pipeline at build time**, + } + ``` + (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. + + ### Write credentials to the env file + +@@ -149,11 +167,13 @@ Resolve two concrete commands for this project: the production **build** command + - **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). + - **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). + - **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). + - **Flutter** One pair per platform you wired: + - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. + - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. + - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. +-- **React Native** Run: `npx react-native run-ios` / `npx react-native run-android`. + + ### Set up CI for automatic uploads + +@@ -328,7 +348,7 @@ Optionally add a temporary, clearly-labeled affordance that captures one test ex + #### Examples + - **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. + - **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. +-- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. + - **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: + ```kotlin + import com.posthog.PostHog +@@ -347,6 +367,20 @@ Optionally add a temporary, clearly-labeled affordance that captures one test ex + ``` + (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. + - **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. + + ### Verify and hand off + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md +index 0d26b2b..51bfb01 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ + # Upload source maps with CLI - Docs + + ## AI wizard +@@ -135,9 +141,9 @@ Set up source map uploading automatically with our wizard by running this comman + + Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md +index 677883f..4791232 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Next.js - Docs ++ ++Copy page ++ + # Upload source maps for Next.js - Docs + + ## AI wizard +@@ -98,9 +104,9 @@ Set up source map uploading automatically with our wizard by running this comman + //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c + ``` + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md +index d40eabb..b6ae318 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ + # Upload source maps - Docs + + If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. +@@ -34,8 +40,12 @@ Otherwise, choose your platform below for manual instructions. + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) + ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) + ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) +@@ -48,9 +58,9 @@ Otherwise, choose your platform below for manual instructions. + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/.gitignore b/.gitignore +index 2b06d22..1524f65 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -5,3 +5,4 @@ out + *.log + .DS_Store + next-env.d.ts ++.env.local +diff --git a/next.config.ts b/next.config.ts +index 2f6491c..09e2218 100644 +--- a/next.config.ts ++++ b/next.config.ts +@@ -1,7 +1,14 @@ + import type { NextConfig } from "next"; ++import { withPostHogConfig } from "@posthog/nextjs-config"; + + const nextConfig: NextConfig = { + reactStrictMode: true, + }; + +-export default nextConfig; ++export default withPostHogConfig(nextConfig, { ++ personalApiKey: process.env.POSTHOG_API_KEY, ++ projectId: process.env.POSTHOG_PROJECT_ID, ++ sourcemaps: { ++ deleteAfterUpload: true, ++ }, ++}); +diff --git a/package-lock.json b/package-lock.json +index cf6007f..cf2a0de 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -8,6 +8,7 @@ + "name": "next-app", + "version": "0.1.0", + "dependencies": { ++ "@posthog/nextjs-config": "^1.10.0", + "next": "16.2.6", + "posthog-js": "^1.376.2", + "react": "19.1.0", +@@ -496,6 +497,56 @@ + "url": "https://opencollective.com/libvips" + } + }, ++ "node_modules/@jridgewell/gen-mapping": { ++ "version": "0.3.13", ++ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", ++ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/sourcemap-codec": "^1.5.0", ++ "@jridgewell/trace-mapping": "^0.3.24" ++ } ++ }, ++ "node_modules/@jridgewell/resolve-uri": { ++ "version": "3.1.2", ++ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", ++ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6.0.0" ++ } ++ }, ++ "node_modules/@jridgewell/source-map": { ++ "version": "0.3.11", ++ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", ++ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/gen-mapping": "^0.3.5", ++ "@jridgewell/trace-mapping": "^0.3.25" ++ } ++ }, ++ "node_modules/@jridgewell/sourcemap-codec": { ++ "version": "1.5.5", ++ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", ++ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@jridgewell/trace-mapping": { ++ "version": "0.3.31", ++ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", ++ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/resolve-uri": "^3.1.0", ++ "@jridgewell/sourcemap-codec": "^1.4.14" ++ } ++ }, + "node_modules/@next/env": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", +@@ -876,6 +927,49 @@ + "node": ">=14" + } + }, ++ "node_modules/@posthog/cli": { ++ "version": "0.14.1", ++ "resolved": "https://registry.npmjs.org/@posthog/cli/-/cli-0.14.1.tgz", ++ "integrity": "sha512-gTzcKpl9TZLf0LrlLHEjChlPc9LIK1gdQG0alMnX6+b+W1mTD+6nTN0W/MeEzjT4DiKDeK8FPhc1n7dT1tWKFw==", ++ "hasInstallScript": true, ++ "hasShrinkwrap": true, ++ "license": "MIT", ++ "dependencies": { ++ "detect-libc": "^2.1.2" ++ }, ++ "bin": { ++ "posthog-cli": "run-posthog-cli.js" ++ }, ++ "engines": { ++ "node": ">=14.14", ++ "npm": ">=6" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/detect-libc": { ++ "version": "2.1.2", ++ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", ++ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", ++ "license": "Apache-2.0", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/prettier": { ++ "version": "3.8.3", ++ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", ++ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", ++ "extraneous": true, ++ "license": "MIT", ++ "bin": { ++ "prettier": "bin/prettier.cjs" ++ }, ++ "engines": { ++ "node": ">=14" ++ }, ++ "funding": { ++ "url": "https://github.com/prettier/prettier?sponsor=1" ++ } ++ }, + "node_modules/@posthog/core": { + "version": "1.29.11", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.29.11.tgz", +@@ -885,12 +979,68 @@ + "@posthog/types": "1.376.2" + } + }, ++ "node_modules/@posthog/nextjs-config": { ++ "version": "1.10.0", ++ "resolved": "https://registry.npmjs.org/@posthog/nextjs-config/-/nextjs-config-1.10.0.tgz", ++ "integrity": "sha512-BlAd8WJlZKvBrKtJjbRNUWAp/KtRMrZrXjwhpfeCj68/cphulOaxig7by84YN7Vsyfk9DMQNGJrJTcE+lcLT7g==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/plugin-utils": "^1.2.0", ++ "@posthog/webpack-plugin": "^1.6.0", ++ "semver": "^7.8.5" ++ }, ++ "engines": { ++ "node": "^20.20.0 || >=22.22.0" ++ }, ++ "peerDependencies": { ++ "next": ">12.1.0" ++ } ++ }, ++ "node_modules/@posthog/plugin-utils": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@posthog/plugin-utils/-/plugin-utils-1.2.0.tgz", ++ "integrity": "sha512-SXG2oVxPnliYKmixyIYqPv1CA4UYPZy9fQL5H+mvN/OQpKioRTawp8I2ofQmf6SfiYpnf+KAzLiRUlK7S3rOCw==", ++ "license": "MIT", ++ "dependencies": { ++ "cross-spawn": "^7.0.6" ++ } ++ }, + "node_modules/@posthog/types": { + "version": "1.376.2", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.376.2.tgz", + "integrity": "sha512-Y3ROpAxNqgcy2G0w6JoG5Gt+P6WNY2lkHTPMPzWqexRwemYbFegDi5AifDyD9/tstKTlOYKTTExtaJ5EBcghyQ==", + "license": "MIT" + }, ++ "node_modules/@posthog/webpack-plugin": { ++ "version": "1.6.0", ++ "resolved": "https://registry.npmjs.org/@posthog/webpack-plugin/-/webpack-plugin-1.6.0.tgz", ++ "integrity": "sha512-bzecfl7al1xyzjC/hZZv6j8Q+jBEe5FIY1p6ggmMwl614Np9HjqV0Am18Gdc0ImEaL12iBxbpx7i9zUc3c/+bw==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/core": "^1.48.8", ++ "@posthog/plugin-utils": "^1.2.0" ++ }, ++ "peerDependencies": { ++ "webpack": "^5" ++ } ++ }, ++ "node_modules/@posthog/webpack-plugin/node_modules/@posthog/core": { ++ "version": "1.49.0", ++ "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.49.0.tgz", ++ "integrity": "sha512-+Ejf6sZ2wI9F37rOrPxoI90mydOw5O/YKjAgWMkkuuQRHGwyfXugxsD/+CVI4Yymg8LujHr0C/KXJvWDJYgwWQ==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/types": "^1.407.0" ++ } ++ }, ++ "node_modules/@posthog/webpack-plugin/node_modules/@posthog/types": { ++ "version": "1.407.0", ++ "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.407.0.tgz", ++ "integrity": "sha512-7J/aFVi7JWFt/ekGVsMFvkTcADoE9MTNf1N9cpBSMUQ/SPLiBjbg386Fzk5OlgWG/u5OemBy4wlgD7YebqeppQ==", ++ "license": "MIT" ++ }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", +@@ -963,6 +1113,20 @@ + "tslib": "^2.8.0" + } + }, ++ "node_modules/@types/estree": { ++ "version": "1.0.9", ++ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", ++ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@types/json-schema": { ++ "version": "7.0.15", ++ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", ++ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", +@@ -999,10 +1163,246 @@ + "license": "MIT", + "optional": true + }, ++ "node_modules/@webassemblyjs/ast": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", ++ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/helper-numbers": "1.13.2", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/floating-point-hex-parser": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", ++ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-api-error": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", ++ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-buffer": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", ++ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-numbers": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", ++ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/floating-point-hex-parser": "1.13.2", ++ "@webassemblyjs/helper-api-error": "1.13.2", ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/helper-wasm-bytecode": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", ++ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-wasm-section": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", ++ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/wasm-gen": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/ieee754": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", ++ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@xtuc/ieee754": "^1.2.0" ++ } ++ }, ++ "node_modules/@webassemblyjs/leb128": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", ++ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", ++ "license": "Apache-2.0", ++ "peer": true, ++ "dependencies": { ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/utf8": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", ++ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/wasm-edit": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", ++ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/helper-wasm-section": "1.14.1", ++ "@webassemblyjs/wasm-gen": "1.14.1", ++ "@webassemblyjs/wasm-opt": "1.14.1", ++ "@webassemblyjs/wasm-parser": "1.14.1", ++ "@webassemblyjs/wast-printer": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-gen": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", ++ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/ieee754": "1.13.2", ++ "@webassemblyjs/leb128": "1.13.2", ++ "@webassemblyjs/utf8": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-opt": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", ++ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/wasm-gen": "1.14.1", ++ "@webassemblyjs/wasm-parser": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-parser": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", ++ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-api-error": "1.13.2", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/ieee754": "1.13.2", ++ "@webassemblyjs/leb128": "1.13.2", ++ "@webassemblyjs/utf8": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/wast-printer": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", ++ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@xtuc/ieee754": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", ++ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", ++ "license": "BSD-3-Clause", ++ "peer": true ++ }, ++ "node_modules/@xtuc/long": { ++ "version": "4.2.2", ++ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", ++ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", ++ "license": "Apache-2.0", ++ "peer": true ++ }, ++ "node_modules/acorn": { ++ "version": "8.18.0", ++ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", ++ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", ++ "license": "MIT", ++ "peer": true, ++ "bin": { ++ "acorn": "bin/acorn" ++ }, ++ "engines": { ++ "node": ">=0.4.0" ++ } ++ }, ++ "node_modules/ajv": { ++ "version": "8.20.0", ++ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", ++ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "fast-deep-equal": "^3.1.3", ++ "fast-uri": "^3.0.1", ++ "json-schema-traverse": "^1.0.0", ++ "require-from-string": "^2.0.2" ++ }, ++ "funding": { ++ "type": "github", ++ "url": "https://github.com/sponsors/epoberezkin" ++ } ++ }, ++ "node_modules/ajv-formats": { ++ "version": "2.1.1", ++ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", ++ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "ajv": "^8.0.0" ++ }, ++ "peerDependencies": { ++ "ajv": "^8.0.0" ++ }, ++ "peerDependenciesMeta": { ++ "ajv": { ++ "optional": true ++ } ++ } ++ }, ++ "node_modules/ajv-keywords": { ++ "version": "5.1.0", ++ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", ++ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "fast-deep-equal": "^3.1.3" ++ }, ++ "peerDependencies": { ++ "ajv": "^8.8.2" ++ } ++ }, + "node_modules/baseline-browser-mapping": { +- "version": "2.10.31", +- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", +- "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", ++ "version": "2.11.19", ++ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", ++ "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" +@@ -1011,10 +1411,51 @@ + "node": ">=6.0.0" + } + }, ++ "node_modules/browserslist": { ++ "version": "4.28.8", ++ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", ++ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", ++ "funding": [ ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/browserslist" ++ }, ++ { ++ "type": "tidelift", ++ "url": "https://tidelift.com/funding/github/npm/browserslist" ++ }, ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/ai" ++ } ++ ], ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "baseline-browser-mapping": "^2.11.12", ++ "caniuse-lite": "^1.0.30001809", ++ "electron-to-chromium": "^1.5.402", ++ "node-releases": "^2.0.53", ++ "update-browserslist-db": "^1.3.0" ++ }, ++ "bin": { ++ "browserslist": "cli.js" ++ }, ++ "engines": { ++ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" ++ } ++ }, ++ "node_modules/buffer-from": { ++ "version": "1.1.2", ++ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", ++ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/caniuse-lite": { +- "version": "1.0.30001793", +- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", +- "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", ++ "version": "1.0.30001810", ++ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", ++ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", +@@ -1031,12 +1472,29 @@ + ], + "license": "CC-BY-4.0" + }, ++ "node_modules/chrome-trace-event": { ++ "version": "1.0.4", ++ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", ++ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6.0" ++ } ++ }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, ++ "node_modules/commander": { ++ "version": "2.20.3", ++ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", ++ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", +@@ -1048,6 +1506,20 @@ + "url": "https://opencollective.com/core-js" + } + }, ++ "node_modules/cross-spawn": { ++ "version": "7.0.6", ++ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ++ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", ++ "license": "MIT", ++ "dependencies": { ++ "path-key": "^3.1.0", ++ "shebang-command": "^2.0.0", ++ "which": "^2.0.1" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } ++ }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", +@@ -1060,7 +1532,6 @@ + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", +- "optional": true, + "engines": { + "node": ">=8" + } +@@ -1074,18 +1545,213 @@ + "@types/trusted-types": "^2.0.7" + } + }, ++ "node_modules/electron-to-chromium": { ++ "version": "1.5.415", ++ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", ++ "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", ++ "license": "ISC", ++ "peer": true ++ }, ++ "node_modules/enhanced-resolve": { ++ "version": "5.24.5", ++ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", ++ "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "graceful-fs": "^4.2.4", ++ "tapable": "^2.3.3" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, ++ "node_modules/es-module-lexer": { ++ "version": "2.3.2", ++ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", ++ "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/escalade": { ++ "version": "3.2.0", ++ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", ++ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6" ++ } ++ }, ++ "node_modules/events": { ++ "version": "3.3.0", ++ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", ++ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=0.8.x" ++ } ++ }, ++ "node_modules/fast-deep-equal": { ++ "version": "3.1.3", ++ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", ++ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/fast-uri": { ++ "version": "3.1.6", ++ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", ++ "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", ++ "funding": [ ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/fastify" ++ }, ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/fastify" ++ } ++ ], ++ "license": "BSD-3-Clause", ++ "peer": true ++ }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, ++ "node_modules/graceful-fs": { ++ "version": "4.2.11", ++ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", ++ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", ++ "license": "ISC", ++ "peer": true ++ }, ++ "node_modules/has-flag": { ++ "version": "4.0.0", ++ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", ++ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/isexe": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", ++ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", ++ "license": "ISC" ++ }, ++ "node_modules/jest-worker": { ++ "version": "27.5.1", ++ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", ++ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/node": "*", ++ "merge-stream": "^2.0.0", ++ "supports-color": "^8.0.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ } ++ }, ++ "node_modules/json-schema-traverse": { ++ "version": "1.0.0", ++ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", ++ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, ++ "node_modules/merge-stream": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", ++ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/mime-db": { ++ "version": "1.54.0", ++ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", ++ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">= 0.6" ++ } ++ }, ++ "node_modules/minimizer-webpack-plugin": { ++ "version": "5.8.0", ++ "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.8.0.tgz", ++ "integrity": "sha512-2cT9+goJfBhtMz+gJqejSf09ClgmYhPhccRb/fb0ztVbixt0BkO8mRuI26FoPPuUNkRk6iEDryWAIouc5W8eJA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/trace-mapping": "^0.3.31", ++ "jest-worker": "^27.4.5", ++ "schema-utils": "^4.3.3", ++ "terser": "^5.51.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ }, ++ "peerDependencies": { ++ "webpack": "^5.1.0" ++ }, ++ "peerDependenciesMeta": { ++ "@minify-html/node": { ++ "optional": true ++ }, ++ "@swc/core": { ++ "optional": true ++ }, ++ "@swc/css": { ++ "optional": true ++ }, ++ "@swc/html": { ++ "optional": true ++ }, ++ "clean-css": { ++ "optional": true ++ }, ++ "cssnano": { ++ "optional": true ++ }, ++ "csso": { ++ "optional": true ++ }, ++ "esbuild": { ++ "optional": true ++ }, ++ "html-minifier-terser": { ++ "optional": true ++ }, ++ "lightningcss": { ++ "optional": true ++ }, ++ "postcss": { ++ "optional": true ++ }, ++ "uglify-js": { ++ "optional": true ++ } ++ } ++ }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", +@@ -1104,6 +1770,13 @@ + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, ++ "node_modules/neo-async": { ++ "version": "2.6.2", ++ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", ++ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", +@@ -1157,6 +1830,25 @@ + } + } + }, ++ "node_modules/node-releases": { ++ "version": "2.0.54", ++ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", ++ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=18" ++ } ++ }, ++ "node_modules/path-key": { ++ "version": "3.1.1", ++ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", ++ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", +@@ -1273,18 +1965,47 @@ + "react": "^19.1.0" + } + }, ++ "node_modules/require-from-string": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", ++ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=0.10.0" ++ } ++ }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, ++ "node_modules/schema-utils": { ++ "version": "4.3.3", ++ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", ++ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/json-schema": "^7.0.9", ++ "ajv": "^8.9.0", ++ "ajv-formats": "^2.1.1", ++ "ajv-keywords": "^5.1.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ } ++ }, + "node_modules/semver": { +- "version": "7.8.0", +- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", +- "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", ++ "version": "7.8.5", ++ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", ++ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", +- "optional": true, + "bin": { + "semver": "bin/semver.js" + }, +@@ -1337,6 +2058,37 @@ + "@img/sharp-win32-x64": "0.34.5" + } + }, ++ "node_modules/shebang-command": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", ++ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", ++ "license": "MIT", ++ "dependencies": { ++ "shebang-regex": "^3.0.0" ++ }, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/shebang-regex": { ++ "version": "3.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", ++ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/source-map": { ++ "version": "0.6.1", ++ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", ++ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", ++ "license": "BSD-3-Clause", ++ "peer": true, ++ "engines": { ++ "node": ">=0.10.0" ++ } ++ }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", +@@ -1346,6 +2098,17 @@ + "node": ">=0.10.0" + } + }, ++ "node_modules/source-map-support": { ++ "version": "0.5.21", ++ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", ++ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "buffer-from": "^1.0.0", ++ "source-map": "^0.6.0" ++ } ++ }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", +@@ -1369,6 +2132,55 @@ + } + } + }, ++ "node_modules/supports-color": { ++ "version": "8.1.1", ++ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", ++ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "has-flag": "^4.0.0" ++ }, ++ "engines": { ++ "node": ">=10" ++ }, ++ "funding": { ++ "url": "https://github.com/chalk/supports-color?sponsor=1" ++ } ++ }, ++ "node_modules/tapable": { ++ "version": "2.3.3", ++ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", ++ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ } ++ }, ++ "node_modules/terser": { ++ "version": "5.51.2", ++ "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", ++ "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", ++ "license": "BSD-2-Clause", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/source-map": "^0.3.3", ++ "acorn": "^8.15.0", ++ "commander": "^2.20.0", ++ "source-map-support": "~0.5.20" ++ }, ++ "bin": { ++ "terser": "bin/terser" ++ }, ++ "engines": { ++ "node": ">=10" ++ } ++ }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", +@@ -1395,11 +2207,123 @@ + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, ++ "node_modules/update-browserslist-db": { ++ "version": "1.3.1", ++ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", ++ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", ++ "funding": [ ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/browserslist" ++ }, ++ { ++ "type": "tidelift", ++ "url": "https://tidelift.com/funding/github/npm/browserslist" ++ }, ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/ai" ++ } ++ ], ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "escalade": "^3.2.0", ++ "picocolors": "^1.1.1" ++ }, ++ "bin": { ++ "update-browserslist-db": "cli.js" ++ }, ++ "peerDependencies": { ++ "browserslist": ">= 4.21.0" ++ } ++ }, ++ "node_modules/watchpack": { ++ "version": "2.5.2", ++ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", ++ "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "graceful-fs": "^4.1.2" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, + "node_modules/web-vitals": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz", + "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==", + "license": "Apache-2.0" ++ }, ++ "node_modules/webpack": { ++ "version": "5.110.0", ++ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.0.tgz", ++ "integrity": "sha512-vzGjrgzXNYRs0MBVkdF288cJt0RIJMxlZy+3pLFo6KIeZI57sBGCgjBp+yNQWf5g9c1us34rjivM1sfNLijQYg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/estree": "^1.0.8", ++ "@types/json-schema": "^7.0.15", ++ "@webassemblyjs/ast": "^1.14.1", ++ "@webassemblyjs/wasm-edit": "^1.14.1", ++ "@webassemblyjs/wasm-parser": "^1.14.1", ++ "acorn": "^8.16.0", ++ "browserslist": "^4.28.1", ++ "chrome-trace-event": "^1.0.2", ++ "enhanced-resolve": "^5.24.4", ++ "es-module-lexer": "^2.1.0", ++ "events": "^3.2.0", ++ "graceful-fs": "^4.2.11", ++ "mime-db": "^1.54.0", ++ "minimizer-webpack-plugin": "^5.7.0", ++ "neo-async": "^2.6.2", ++ "schema-utils": "^4.3.3", ++ "tapable": "^2.3.0", ++ "watchpack": "^2.5.2", ++ "webpack-sources": "^3.5.1" ++ }, ++ "bin": { ++ "webpack": "bin/webpack.js" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ }, ++ "peerDependenciesMeta": { ++ "webpack-cli": { ++ "optional": true ++ } ++ } ++ }, ++ "node_modules/webpack-sources": { ++ "version": "3.5.1", ++ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", ++ "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, ++ "node_modules/which": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", ++ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", ++ "license": "ISC", ++ "dependencies": { ++ "isexe": "^2.0.0" ++ }, ++ "bin": { ++ "node-which": "bin/node-which" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } + } + } + } +diff --git a/package.json b/package.json +index 8f41f32..c2ab10b 100644 +--- a/package.json ++++ b/package.json +@@ -9,6 +9,7 @@ + "lint": "next lint" + }, + "dependencies": { ++ "@posthog/nextjs-config": "^1.10.0", + "next": "16.2.6", + "posthog-js": "^1.376.2", + "react": "19.1.0", +diff --git a/posthog-source-maps-report.md b/posthog-source-maps-report.md +new file mode 100644 +index 0000000..8c7c2b1 +--- /dev/null ++++ b/posthog-source-maps-report.md +@@ -0,0 +1,54 @@ ++# PostHog Source Maps Setup — Handoff Report ++ ++## What was configured ++ ++### Files changed ++ ++- **`next.config.ts`** — wrapped the Next.js config with `withPostHogConfig` from `@posthog/nextjs-config`. The wrapper handles source map generation, chunk-ID injection, and upload automatically on every production build. ++- **`.env.local`** — updated `POSTHOG_API_KEY` with your new personal API key (source map upload preset) and confirmed `POSTHOG_PROJECT_ID=228144`. ++- **`package.json`** — `@posthog/nextjs-config` added as a dependency. ++ ++### How it works ++ ++On every `npm run build`, the `withPostHogConfig` wrapper: ++1. Emits source maps alongside your JS bundles ++2. Injects a `//# chunkId=…` comment into each bundle (so PostHog can match uploaded maps to captured errors) ++3. Uploads the maps to PostHog Error Tracking ++4. Deletes the `.map` files from the output so they aren't served publicly ++ ++## Build and run commands ++ ++| Step | Command | ++|------|---------| ++| Production build (uploads source maps) | `npm run build` | ++| Run the built app | `npm run start` | ++ ++## Environment variables set ++ ++| Variable | Purpose | ++|----------|---------| ++| `POSTHOG_API_KEY` | Personal API key with error-tracking write access (updated in `.env.local`) | ++| `POSTHOG_PROJECT_ID` | PostHog project ID — `228144` | ++ ++## CI/CD: action required ++ ++No CI config file was found in this project (no `.github/workflows/`, `Dockerfile`, `.gitlab-ci.yml`, etc.). Wherever your production build runs in CI or on a hosting platform (Vercel, Netlify, Railway, etc.), you must add these variables as secrets/environment variables so source maps upload on every deploy: ++ ++| Variable | Where to set | ++|----------|-------------| ++| `POSTHOG_API_KEY` | CI/CD secrets (e.g. GitHub: Settings → Secrets and variables → Actions; Vercel: Project → Settings → Environment Variables) | ++| `POSTHOG_PROJECT_ID` | Same — value is `228144` (not sensitive, but must be present) | ++ ++**Important:** Do not commit `.env.local` to version control. The personal API key must only live in `.env.local` locally and in your CI provider's secret store. ++ ++## Verify the upload ++ ++After running `npm run build`, check that a new symbol set appears here: ++ ++https://us.posthog.com/project/228144/error_tracking/configuration ++ ++A new entry should appear within a few seconds of the build completing. If it doesn't, confirm `POSTHOG_API_KEY` and `POSTHOG_PROJECT_ID` are readable in the build environment. ++ ++## Test affordance ++ ++A temporary "Test PostHog Error Tracking" button was added to `app/page.tsx` and then **reverted** after you confirmed the test. No test code remains in the project. diff --git a/results/source-maps-sol-medium/next__anthropic/result.json b/results/source-maps-sol-medium/next__anthropic/result.json new file mode 100644 index 000000000..37e26b449 --- /dev/null +++ b/results/source-maps-sol-medium/next__anthropic/result.json @@ -0,0 +1,24 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "@posthog/nextjs-config", + "posthog-js" + ], + "envFile": "/tmp/sm-run-next__anthropic/.env.local", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file diff --git a/results/source-maps-sol-medium/next__sol-medium/app.diff b/results/source-maps-sol-medium/next__sol-medium/app.diff new file mode 100644 index 000000000..18a9579f3 --- /dev/null +++ b/results/source-maps-sol-medium/next__sol-medium/app.diff @@ -0,0 +1,1353 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md +index 897a35c..6a58547 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/SKILL.md +@@ -3,7 +3,7 @@ name: error-tracking-upload-source-maps-nextjs + description: Upload source maps to PostHog Error Tracking for Next.js + metadata: + author: PostHog +- version: 1.37.1 ++ version: 1.49.1 + --- + + # Upload source maps to PostHog for Next.js +@@ -17,7 +17,7 @@ This skill helps you upload source maps (or platform debug symbols) so PostHog E + - `references/cli.md` - Upload source maps with cli - docs + - `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow + +-The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. + + ## Steps + +@@ -59,8 +59,22 @@ Wire source map generation, chunk-ID injection, and upload into your **productio + 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. + 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. + 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. + - **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. +-- **React Native** You upload platform debug symbols (Hermes maps, dSYMs) rather than plain `.js.map` files — follow the platform reference for the exact build hook. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. + - **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. + - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. + - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. +@@ -80,10 +94,12 @@ The upload credentials must be readable **by the build pipeline at build time**, + - **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) + - **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. + - **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. + - **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: + - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). + - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. + - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. + + #### Examples + - **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. +@@ -122,6 +138,8 @@ The upload credentials must be readable **by the build pipeline at build time**, + } + ``` + (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. + + ### Write credentials to the env file + +@@ -149,11 +167,13 @@ Resolve two concrete commands for this project: the production **build** command + - **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). + - **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). + - **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). + - **Flutter** One pair per platform you wired: + - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. + - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. + - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. +-- **React Native** Run: `npx react-native run-ios` / `npx react-native run-android`. + + ### Set up CI for automatic uploads + +@@ -328,7 +348,7 @@ Optionally add a temporary, clearly-labeled affordance that captures one test ex + #### Examples + - **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. + - **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. +-- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. + - **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: + ```kotlin + import com.posthog.PostHog +@@ -347,6 +367,20 @@ Optionally add a temporary, clearly-labeled affordance that captures one test ex + ``` + (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. + - **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. + + ### Verify and hand off + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md +index 0d26b2b..51bfb01 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/cli.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ + # Upload source maps with CLI - Docs + + ## AI wizard +@@ -135,9 +141,9 @@ Set up source map uploading automatically with our wizard by running this comman + + Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md +index 677883f..4791232 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/nextjs.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Next.js - Docs ++ ++Copy page ++ + # Upload source maps for Next.js - Docs + + ## AI wizard +@@ -98,9 +104,9 @@ Set up source map uploading automatically with our wizard by running this comman + //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c + ``` + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md +index d40eabb..b6ae318 100644 +--- a/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md ++++ b/.claude/skills/error-tracking-upload-source-maps-nextjs/references/upload-source-maps.md +@@ -1,3 +1,9 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ + # Upload source maps - Docs + + If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. +@@ -34,8 +40,12 @@ Otherwise, choose your platform below for manual instructions. + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) + ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) + ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) +@@ -48,9 +58,9 @@ Otherwise, choose your platform below for manual instructions. + + - [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) + +-### Community questions ++### Still have questions? + +-Ask a question ++Ask PostHog AI + + ### Was this page useful? + +diff --git a/next.config.ts b/next.config.ts +index 2f6491c..44cdc95 100644 +--- a/next.config.ts ++++ b/next.config.ts +@@ -1,7 +1,16 @@ ++import { withPostHogConfig } from "@posthog/nextjs-config"; + import type { NextConfig } from "next"; + + const nextConfig: NextConfig = { + reactStrictMode: true, + }; + +-export default nextConfig; ++export default withPostHogConfig(nextConfig, { ++ personalApiKey: process.env.POSTHOG_API_KEY, ++ projectId: process.env.POSTHOG_PROJECT_ID, ++ host: process.env.NEXT_PUBLIC_POSTHOG_HOST, ++ sourcemaps: { ++ enabled: true, ++ deleteAfterUpload: true, ++ }, ++}); +diff --git a/package-lock.json b/package-lock.json +index cf6007f..cf2a0de 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -8,6 +8,7 @@ + "name": "next-app", + "version": "0.1.0", + "dependencies": { ++ "@posthog/nextjs-config": "^1.10.0", + "next": "16.2.6", + "posthog-js": "^1.376.2", + "react": "19.1.0", +@@ -496,6 +497,56 @@ + "url": "https://opencollective.com/libvips" + } + }, ++ "node_modules/@jridgewell/gen-mapping": { ++ "version": "0.3.13", ++ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", ++ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/sourcemap-codec": "^1.5.0", ++ "@jridgewell/trace-mapping": "^0.3.24" ++ } ++ }, ++ "node_modules/@jridgewell/resolve-uri": { ++ "version": "3.1.2", ++ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", ++ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6.0.0" ++ } ++ }, ++ "node_modules/@jridgewell/source-map": { ++ "version": "0.3.11", ++ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", ++ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/gen-mapping": "^0.3.5", ++ "@jridgewell/trace-mapping": "^0.3.25" ++ } ++ }, ++ "node_modules/@jridgewell/sourcemap-codec": { ++ "version": "1.5.5", ++ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", ++ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@jridgewell/trace-mapping": { ++ "version": "0.3.31", ++ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", ++ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/resolve-uri": "^3.1.0", ++ "@jridgewell/sourcemap-codec": "^1.4.14" ++ } ++ }, + "node_modules/@next/env": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz", +@@ -876,6 +927,49 @@ + "node": ">=14" + } + }, ++ "node_modules/@posthog/cli": { ++ "version": "0.14.1", ++ "resolved": "https://registry.npmjs.org/@posthog/cli/-/cli-0.14.1.tgz", ++ "integrity": "sha512-gTzcKpl9TZLf0LrlLHEjChlPc9LIK1gdQG0alMnX6+b+W1mTD+6nTN0W/MeEzjT4DiKDeK8FPhc1n7dT1tWKFw==", ++ "hasInstallScript": true, ++ "hasShrinkwrap": true, ++ "license": "MIT", ++ "dependencies": { ++ "detect-libc": "^2.1.2" ++ }, ++ "bin": { ++ "posthog-cli": "run-posthog-cli.js" ++ }, ++ "engines": { ++ "node": ">=14.14", ++ "npm": ">=6" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/detect-libc": { ++ "version": "2.1.2", ++ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", ++ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", ++ "license": "Apache-2.0", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/prettier": { ++ "version": "3.8.3", ++ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", ++ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", ++ "extraneous": true, ++ "license": "MIT", ++ "bin": { ++ "prettier": "bin/prettier.cjs" ++ }, ++ "engines": { ++ "node": ">=14" ++ }, ++ "funding": { ++ "url": "https://github.com/prettier/prettier?sponsor=1" ++ } ++ }, + "node_modules/@posthog/core": { + "version": "1.29.11", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.29.11.tgz", +@@ -885,12 +979,68 @@ + "@posthog/types": "1.376.2" + } + }, ++ "node_modules/@posthog/nextjs-config": { ++ "version": "1.10.0", ++ "resolved": "https://registry.npmjs.org/@posthog/nextjs-config/-/nextjs-config-1.10.0.tgz", ++ "integrity": "sha512-BlAd8WJlZKvBrKtJjbRNUWAp/KtRMrZrXjwhpfeCj68/cphulOaxig7by84YN7Vsyfk9DMQNGJrJTcE+lcLT7g==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/plugin-utils": "^1.2.0", ++ "@posthog/webpack-plugin": "^1.6.0", ++ "semver": "^7.8.5" ++ }, ++ "engines": { ++ "node": "^20.20.0 || >=22.22.0" ++ }, ++ "peerDependencies": { ++ "next": ">12.1.0" ++ } ++ }, ++ "node_modules/@posthog/plugin-utils": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@posthog/plugin-utils/-/plugin-utils-1.2.0.tgz", ++ "integrity": "sha512-SXG2oVxPnliYKmixyIYqPv1CA4UYPZy9fQL5H+mvN/OQpKioRTawp8I2ofQmf6SfiYpnf+KAzLiRUlK7S3rOCw==", ++ "license": "MIT", ++ "dependencies": { ++ "cross-spawn": "^7.0.6" ++ } ++ }, + "node_modules/@posthog/types": { + "version": "1.376.2", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.376.2.tgz", + "integrity": "sha512-Y3ROpAxNqgcy2G0w6JoG5Gt+P6WNY2lkHTPMPzWqexRwemYbFegDi5AifDyD9/tstKTlOYKTTExtaJ5EBcghyQ==", + "license": "MIT" + }, ++ "node_modules/@posthog/webpack-plugin": { ++ "version": "1.6.0", ++ "resolved": "https://registry.npmjs.org/@posthog/webpack-plugin/-/webpack-plugin-1.6.0.tgz", ++ "integrity": "sha512-bzecfl7al1xyzjC/hZZv6j8Q+jBEe5FIY1p6ggmMwl614Np9HjqV0Am18Gdc0ImEaL12iBxbpx7i9zUc3c/+bw==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/core": "^1.48.8", ++ "@posthog/plugin-utils": "^1.2.0" ++ }, ++ "peerDependencies": { ++ "webpack": "^5" ++ } ++ }, ++ "node_modules/@posthog/webpack-plugin/node_modules/@posthog/core": { ++ "version": "1.49.0", ++ "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.49.0.tgz", ++ "integrity": "sha512-+Ejf6sZ2wI9F37rOrPxoI90mydOw5O/YKjAgWMkkuuQRHGwyfXugxsD/+CVI4Yymg8LujHr0C/KXJvWDJYgwWQ==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/types": "^1.407.0" ++ } ++ }, ++ "node_modules/@posthog/webpack-plugin/node_modules/@posthog/types": { ++ "version": "1.407.0", ++ "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.407.0.tgz", ++ "integrity": "sha512-7J/aFVi7JWFt/ekGVsMFvkTcADoE9MTNf1N9cpBSMUQ/SPLiBjbg386Fzk5OlgWG/u5OemBy4wlgD7YebqeppQ==", ++ "license": "MIT" ++ }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", +@@ -963,6 +1113,20 @@ + "tslib": "^2.8.0" + } + }, ++ "node_modules/@types/estree": { ++ "version": "1.0.9", ++ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", ++ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@types/json-schema": { ++ "version": "7.0.15", ++ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", ++ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", +@@ -999,10 +1163,246 @@ + "license": "MIT", + "optional": true + }, ++ "node_modules/@webassemblyjs/ast": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", ++ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/helper-numbers": "1.13.2", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/floating-point-hex-parser": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", ++ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-api-error": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", ++ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-buffer": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", ++ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-numbers": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", ++ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/floating-point-hex-parser": "1.13.2", ++ "@webassemblyjs/helper-api-error": "1.13.2", ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/helper-wasm-bytecode": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", ++ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/helper-wasm-section": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", ++ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/wasm-gen": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/ieee754": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", ++ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@xtuc/ieee754": "^1.2.0" ++ } ++ }, ++ "node_modules/@webassemblyjs/leb128": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", ++ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", ++ "license": "Apache-2.0", ++ "peer": true, ++ "dependencies": { ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/utf8": { ++ "version": "1.13.2", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", ++ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/@webassemblyjs/wasm-edit": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", ++ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/helper-wasm-section": "1.14.1", ++ "@webassemblyjs/wasm-gen": "1.14.1", ++ "@webassemblyjs/wasm-opt": "1.14.1", ++ "@webassemblyjs/wasm-parser": "1.14.1", ++ "@webassemblyjs/wast-printer": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-gen": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", ++ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/ieee754": "1.13.2", ++ "@webassemblyjs/leb128": "1.13.2", ++ "@webassemblyjs/utf8": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-opt": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", ++ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-buffer": "1.14.1", ++ "@webassemblyjs/wasm-gen": "1.14.1", ++ "@webassemblyjs/wasm-parser": "1.14.1" ++ } ++ }, ++ "node_modules/@webassemblyjs/wasm-parser": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", ++ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@webassemblyjs/helper-api-error": "1.13.2", ++ "@webassemblyjs/helper-wasm-bytecode": "1.13.2", ++ "@webassemblyjs/ieee754": "1.13.2", ++ "@webassemblyjs/leb128": "1.13.2", ++ "@webassemblyjs/utf8": "1.13.2" ++ } ++ }, ++ "node_modules/@webassemblyjs/wast-printer": { ++ "version": "1.14.1", ++ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", ++ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@webassemblyjs/ast": "1.14.1", ++ "@xtuc/long": "4.2.2" ++ } ++ }, ++ "node_modules/@xtuc/ieee754": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", ++ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", ++ "license": "BSD-3-Clause", ++ "peer": true ++ }, ++ "node_modules/@xtuc/long": { ++ "version": "4.2.2", ++ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", ++ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", ++ "license": "Apache-2.0", ++ "peer": true ++ }, ++ "node_modules/acorn": { ++ "version": "8.18.0", ++ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", ++ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", ++ "license": "MIT", ++ "peer": true, ++ "bin": { ++ "acorn": "bin/acorn" ++ }, ++ "engines": { ++ "node": ">=0.4.0" ++ } ++ }, ++ "node_modules/ajv": { ++ "version": "8.20.0", ++ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", ++ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "fast-deep-equal": "^3.1.3", ++ "fast-uri": "^3.0.1", ++ "json-schema-traverse": "^1.0.0", ++ "require-from-string": "^2.0.2" ++ }, ++ "funding": { ++ "type": "github", ++ "url": "https://github.com/sponsors/epoberezkin" ++ } ++ }, ++ "node_modules/ajv-formats": { ++ "version": "2.1.1", ++ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", ++ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "ajv": "^8.0.0" ++ }, ++ "peerDependencies": { ++ "ajv": "^8.0.0" ++ }, ++ "peerDependenciesMeta": { ++ "ajv": { ++ "optional": true ++ } ++ } ++ }, ++ "node_modules/ajv-keywords": { ++ "version": "5.1.0", ++ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", ++ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "fast-deep-equal": "^3.1.3" ++ }, ++ "peerDependencies": { ++ "ajv": "^8.8.2" ++ } ++ }, + "node_modules/baseline-browser-mapping": { +- "version": "2.10.31", +- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.31.tgz", +- "integrity": "sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==", ++ "version": "2.11.19", ++ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.19.tgz", ++ "integrity": "sha512-Grytf1xOxOEMTGRwx6rLGKkTabd4vMg3VrKdj/7joCmV0qgh4QwMMO6xh34YEXQqirAuUdgQGa5orJQQ+69RBw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" +@@ -1011,10 +1411,51 @@ + "node": ">=6.0.0" + } + }, ++ "node_modules/browserslist": { ++ "version": "4.28.8", ++ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", ++ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", ++ "funding": [ ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/browserslist" ++ }, ++ { ++ "type": "tidelift", ++ "url": "https://tidelift.com/funding/github/npm/browserslist" ++ }, ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/ai" ++ } ++ ], ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "baseline-browser-mapping": "^2.11.12", ++ "caniuse-lite": "^1.0.30001809", ++ "electron-to-chromium": "^1.5.402", ++ "node-releases": "^2.0.53", ++ "update-browserslist-db": "^1.3.0" ++ }, ++ "bin": { ++ "browserslist": "cli.js" ++ }, ++ "engines": { ++ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" ++ } ++ }, ++ "node_modules/buffer-from": { ++ "version": "1.1.2", ++ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", ++ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/caniuse-lite": { +- "version": "1.0.30001793", +- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", +- "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", ++ "version": "1.0.30001810", ++ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", ++ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", +@@ -1031,12 +1472,29 @@ + ], + "license": "CC-BY-4.0" + }, ++ "node_modules/chrome-trace-event": { ++ "version": "1.0.4", ++ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", ++ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6.0" ++ } ++ }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, ++ "node_modules/commander": { ++ "version": "2.20.3", ++ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", ++ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/core-js": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", +@@ -1048,6 +1506,20 @@ + "url": "https://opencollective.com/core-js" + } + }, ++ "node_modules/cross-spawn": { ++ "version": "7.0.6", ++ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ++ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", ++ "license": "MIT", ++ "dependencies": { ++ "path-key": "^3.1.0", ++ "shebang-command": "^2.0.0", ++ "which": "^2.0.1" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } ++ }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", +@@ -1060,7 +1532,6 @@ + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", +- "optional": true, + "engines": { + "node": ">=8" + } +@@ -1074,18 +1545,213 @@ + "@types/trusted-types": "^2.0.7" + } + }, ++ "node_modules/electron-to-chromium": { ++ "version": "1.5.415", ++ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.415.tgz", ++ "integrity": "sha512-958V+Kbhtgz+SxXeEVKBjrlKRBIDAYvUJfwhjxMZ5S6ut9jAl7l9ZKBkBrvjyjZE36PabLUo2L8kEeV5O4vgJg==", ++ "license": "ISC", ++ "peer": true ++ }, ++ "node_modules/enhanced-resolve": { ++ "version": "5.24.5", ++ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", ++ "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "graceful-fs": "^4.2.4", ++ "tapable": "^2.3.3" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, ++ "node_modules/es-module-lexer": { ++ "version": "2.3.2", ++ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", ++ "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/escalade": { ++ "version": "3.2.0", ++ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", ++ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6" ++ } ++ }, ++ "node_modules/events": { ++ "version": "3.3.0", ++ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", ++ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=0.8.x" ++ } ++ }, ++ "node_modules/fast-deep-equal": { ++ "version": "3.1.3", ++ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", ++ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/fast-uri": { ++ "version": "3.1.6", ++ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.6.tgz", ++ "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", ++ "funding": [ ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/fastify" ++ }, ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/fastify" ++ } ++ ], ++ "license": "BSD-3-Clause", ++ "peer": true ++ }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, ++ "node_modules/graceful-fs": { ++ "version": "4.2.11", ++ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", ++ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", ++ "license": "ISC", ++ "peer": true ++ }, ++ "node_modules/has-flag": { ++ "version": "4.0.0", ++ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", ++ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/isexe": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", ++ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", ++ "license": "ISC" ++ }, ++ "node_modules/jest-worker": { ++ "version": "27.5.1", ++ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", ++ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/node": "*", ++ "merge-stream": "^2.0.0", ++ "supports-color": "^8.0.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ } ++ }, ++ "node_modules/json-schema-traverse": { ++ "version": "1.0.0", ++ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", ++ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, ++ "node_modules/merge-stream": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", ++ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", ++ "license": "MIT", ++ "peer": true ++ }, ++ "node_modules/mime-db": { ++ "version": "1.54.0", ++ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", ++ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">= 0.6" ++ } ++ }, ++ "node_modules/minimizer-webpack-plugin": { ++ "version": "5.8.0", ++ "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.8.0.tgz", ++ "integrity": "sha512-2cT9+goJfBhtMz+gJqejSf09ClgmYhPhccRb/fb0ztVbixt0BkO8mRuI26FoPPuUNkRk6iEDryWAIouc5W8eJA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/trace-mapping": "^0.3.31", ++ "jest-worker": "^27.4.5", ++ "schema-utils": "^4.3.3", ++ "terser": "^5.51.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ }, ++ "peerDependencies": { ++ "webpack": "^5.1.0" ++ }, ++ "peerDependenciesMeta": { ++ "@minify-html/node": { ++ "optional": true ++ }, ++ "@swc/core": { ++ "optional": true ++ }, ++ "@swc/css": { ++ "optional": true ++ }, ++ "@swc/html": { ++ "optional": true ++ }, ++ "clean-css": { ++ "optional": true ++ }, ++ "cssnano": { ++ "optional": true ++ }, ++ "csso": { ++ "optional": true ++ }, ++ "esbuild": { ++ "optional": true ++ }, ++ "html-minifier-terser": { ++ "optional": true ++ }, ++ "lightningcss": { ++ "optional": true ++ }, ++ "postcss": { ++ "optional": true ++ }, ++ "uglify-js": { ++ "optional": true ++ } ++ } ++ }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", +@@ -1104,6 +1770,13 @@ + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, ++ "node_modules/neo-async": { ++ "version": "2.6.2", ++ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", ++ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/next": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz", +@@ -1157,6 +1830,25 @@ + } + } + }, ++ "node_modules/node-releases": { ++ "version": "2.0.54", ++ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", ++ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=18" ++ } ++ }, ++ "node_modules/path-key": { ++ "version": "3.1.1", ++ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", ++ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", +@@ -1273,18 +1965,47 @@ + "react": "^19.1.0" + } + }, ++ "node_modules/require-from-string": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", ++ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=0.10.0" ++ } ++ }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, ++ "node_modules/schema-utils": { ++ "version": "4.3.3", ++ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", ++ "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/json-schema": "^7.0.9", ++ "ajv": "^8.9.0", ++ "ajv-formats": "^2.1.1", ++ "ajv-keywords": "^5.1.0" ++ }, ++ "engines": { ++ "node": ">= 10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ } ++ }, + "node_modules/semver": { +- "version": "7.8.0", +- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", +- "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", ++ "version": "7.8.5", ++ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", ++ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", +- "optional": true, + "bin": { + "semver": "bin/semver.js" + }, +@@ -1337,6 +2058,37 @@ + "@img/sharp-win32-x64": "0.34.5" + } + }, ++ "node_modules/shebang-command": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", ++ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", ++ "license": "MIT", ++ "dependencies": { ++ "shebang-regex": "^3.0.0" ++ }, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/shebang-regex": { ++ "version": "3.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", ++ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/source-map": { ++ "version": "0.6.1", ++ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", ++ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", ++ "license": "BSD-3-Clause", ++ "peer": true, ++ "engines": { ++ "node": ">=0.10.0" ++ } ++ }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", +@@ -1346,6 +2098,17 @@ + "node": ">=0.10.0" + } + }, ++ "node_modules/source-map-support": { ++ "version": "0.5.21", ++ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", ++ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "buffer-from": "^1.0.0", ++ "source-map": "^0.6.0" ++ } ++ }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", +@@ -1369,6 +2132,55 @@ + } + } + }, ++ "node_modules/supports-color": { ++ "version": "8.1.1", ++ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", ++ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "has-flag": "^4.0.0" ++ }, ++ "engines": { ++ "node": ">=10" ++ }, ++ "funding": { ++ "url": "https://github.com/chalk/supports-color?sponsor=1" ++ } ++ }, ++ "node_modules/tapable": { ++ "version": "2.3.3", ++ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", ++ "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=6" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ } ++ }, ++ "node_modules/terser": { ++ "version": "5.51.2", ++ "resolved": "https://registry.npmjs.org/terser/-/terser-5.51.2.tgz", ++ "integrity": "sha512-bWnjSNscmuI+GJze6ZupnHP8G/cTcsJF+bXCeQknk2SHQsgbNJnLrqiH9jZ2W4STPVXH2mDKKRX3iwPhc9Cn/Q==", ++ "license": "BSD-2-Clause", ++ "peer": true, ++ "dependencies": { ++ "@jridgewell/source-map": "^0.3.3", ++ "acorn": "^8.15.0", ++ "commander": "^2.20.0", ++ "source-map-support": "~0.5.20" ++ }, ++ "bin": { ++ "terser": "bin/terser" ++ }, ++ "engines": { ++ "node": ">=10" ++ } ++ }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", +@@ -1395,11 +2207,123 @@ + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, ++ "node_modules/update-browserslist-db": { ++ "version": "1.3.1", ++ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", ++ "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", ++ "funding": [ ++ { ++ "type": "opencollective", ++ "url": "https://opencollective.com/browserslist" ++ }, ++ { ++ "type": "tidelift", ++ "url": "https://tidelift.com/funding/github/npm/browserslist" ++ }, ++ { ++ "type": "github", ++ "url": "https://github.com/sponsors/ai" ++ } ++ ], ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "escalade": "^3.2.0", ++ "picocolors": "^1.1.1" ++ }, ++ "bin": { ++ "update-browserslist-db": "cli.js" ++ }, ++ "peerDependencies": { ++ "browserslist": ">= 4.21.0" ++ } ++ }, ++ "node_modules/watchpack": { ++ "version": "2.5.2", ++ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", ++ "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "graceful-fs": "^4.1.2" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, + "node_modules/web-vitals": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz", + "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==", + "license": "Apache-2.0" ++ }, ++ "node_modules/webpack": { ++ "version": "5.110.0", ++ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.0.tgz", ++ "integrity": "sha512-vzGjrgzXNYRs0MBVkdF288cJt0RIJMxlZy+3pLFo6KIeZI57sBGCgjBp+yNQWf5g9c1us34rjivM1sfNLijQYg==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/estree": "^1.0.8", ++ "@types/json-schema": "^7.0.15", ++ "@webassemblyjs/ast": "^1.14.1", ++ "@webassemblyjs/wasm-edit": "^1.14.1", ++ "@webassemblyjs/wasm-parser": "^1.14.1", ++ "acorn": "^8.16.0", ++ "browserslist": "^4.28.1", ++ "chrome-trace-event": "^1.0.2", ++ "enhanced-resolve": "^5.24.4", ++ "es-module-lexer": "^2.1.0", ++ "events": "^3.2.0", ++ "graceful-fs": "^4.2.11", ++ "mime-db": "^1.54.0", ++ "minimizer-webpack-plugin": "^5.7.0", ++ "neo-async": "^2.6.2", ++ "schema-utils": "^4.3.3", ++ "tapable": "^2.3.0", ++ "watchpack": "^2.5.2", ++ "webpack-sources": "^3.5.1" ++ }, ++ "bin": { ++ "webpack": "bin/webpack.js" ++ }, ++ "engines": { ++ "node": ">=10.13.0" ++ }, ++ "funding": { ++ "type": "opencollective", ++ "url": "https://opencollective.com/webpack" ++ }, ++ "peerDependenciesMeta": { ++ "webpack-cli": { ++ "optional": true ++ } ++ } ++ }, ++ "node_modules/webpack-sources": { ++ "version": "3.5.1", ++ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", ++ "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", ++ "license": "MIT", ++ "peer": true, ++ "engines": { ++ "node": ">=10.13.0" ++ } ++ }, ++ "node_modules/which": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", ++ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", ++ "license": "ISC", ++ "dependencies": { ++ "isexe": "^2.0.0" ++ }, ++ "bin": { ++ "node-which": "bin/node-which" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } + } + } + } +diff --git a/package.json b/package.json +index 8f41f32..c2ab10b 100644 +--- a/package.json ++++ b/package.json +@@ -9,6 +9,7 @@ + "lint": "next lint" + }, + "dependencies": { ++ "@posthog/nextjs-config": "^1.10.0", + "next": "16.2.6", + "posthog-js": "^1.376.2", + "react": "19.1.0", diff --git a/results/source-maps-sol-medium/next__sol-medium/result.json b/results/source-maps-sol-medium/next__sol-medium/result.json new file mode 100644 index 000000000..eb10ba941 --- /dev/null +++ b/results/source-maps-sol-medium/next__sol-medium/result.json @@ -0,0 +1,24 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "@posthog/nextjs-config", + "posthog-js" + ], + "envFile": "/tmp/sm-run-next__sol-medium/.env.local", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file diff --git a/results/source-maps-sol-medium/node-raw__anthropic/app.diff b/results/source-maps-sol-medium/node-raw__anthropic/app.diff new file mode 100644 index 000000000..84b6921a0 --- /dev/null +++ b/results/source-maps-sol-medium/node-raw__anthropic/app.diff @@ -0,0 +1,877 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/.posthog-wizard b/.claude/skills/error-tracking-upload-source-maps-node/.posthog-wizard +new file mode 100644 +index 0000000..e69de29 +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md +new file mode 100644 +index 0000000..f3a7670 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md +@@ -0,0 +1,415 @@ ++--- ++name: error-tracking-upload-source-maps-node ++description: Upload source maps to PostHog Error Tracking for Node.js ++metadata: ++ author: PostHog ++ version: 1.49.1 ++--- ++ ++# Upload source maps to PostHog for Node.js ++ ++This skill helps you upload source maps (or platform debug symbols) so PostHog Error Tracking can resolve minified stack traces back to your original source. ++ ++## Reference files ++ ++- `references/node.md` - Upload source maps for Node.js - docs ++- `references/upload-source-maps.md` - Upload source maps - docs ++- `references/cli.md` - Upload source maps with cli - docs ++- `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow ++ ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. ++ ++## Steps ++ ++The stages of wiring up source map upload, in order. Each step has a short overview, gotchas under **Tips**, and per-technology notes under **Examples**. The reference files above are the source of truth for the exact, per-framework API — when this page and a reference disagree, follow the reference for Node.js. ++ ++### Get a personal API key ++ ++Source map upload authenticates with a **personal API key**, not the public project API key the SDK uses at runtime. The key needs error-tracking write access; the quickest path is the "Source map upload" preset on PostHog's personal API keys settings page. ++ ++#### Tips ++- The public project key (the one in your SDK `init`) will **not** work for uploads — it has no write scope for symbol sets. ++- Never hardcode the key in source. It belongs in an environment variable read at build time (see "Write credentials to the env file"). ++- Keys can't be minted programmatically — create them by hand in PostHog settings, then store the value as a secret. ++ ++### Apply build-config changes ++ ++Wire source map generation, chunk-ID injection, and upload into your **production build** so every deploy ships matching maps. Depending on the platform this is either a build/bundler plugin, or a `posthog-cli sourcemap process` step run after the build (it injects chunk IDs and uploads in one pass). Follow the Node.js reference for the exact wiring. ++ ++#### Tips ++- If you wire `posthog-cli` directly (no framework or bundler plugin), generating the maps is **your** responsibility — the CLI only injects chunk IDs into, and uploads, maps your build already produced. Two things must be true before `posthog-cli sourcemap process` works: ++ - Source maps are emitted next to your output bundles (e.g. `.js.map` files). ++ - The maps include `sourcesContent` (the original source embedded inside the map). Without it PostHog has the line/column mappings but not the code, so traces can't be fully resolved. ++- **Inject before deploy**: the *injected* bundles must be the ones shipped to production. Bundles missing the `//# chunkId=…` comment can't be matched to uploaded maps. ++- Wire injection + upload into the build itself (plugin, post-build script, or CI step) — manual uploads drift from deployed code. ++- **Don't ship source maps publicly**: omit `.map` files from the deployed artifact, or use hidden source maps. Uploaded maps live in PostHog, not on your origin. ++- **Link each release to its commit.** The CLI auto-detects the commit from the CI's git env vars — see "Associate the release with a git commit" for making those reachable in Docker/CI builds. ++ ++#### Examples ++- **Node / tsc** Emit maps with embedded sources by setting both in `tsconfig.json`: `"sourceMap": true` and `"inlineSources": true`. Then run `posthog-cli sourcemap process` against the build output dir as a post-build step — it injects chunk IDs and uploads in one pass, and needs the upload credentials (see "Make credentials available at build time"). ++- **Vite / Webpack / Rollup** Prefer the bundler plugin from the reference over hand-rolling the CLI — it injects and uploads in one pass. Make sure the bundler is configured to emit source maps. ++- **iOS (Xcode)** iOS uploads **dSYM debug symbols**, not source maps. Required target changes: ++ 1. `DEBUG_INFORMATION_FORMAT = dwarf-with-dsym` for Release. ++ 2. `ENABLE_USER_SCRIPT_SANDBOXING = NO`. ++ 3. A Run Script phase, ordered last, with `$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)` in its Input Files, calling the SDK's bundled script — do not hand-roll the upload: ++ - SPM: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/posthog-ios/build-tools/upload-symbols.sh"` ++ - CocoaPods: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh"` ++ Copy the invocation verbatim — the `POSTHOG_INCLUDE_SOURCE=1` and `POSTHOG_CLI_DOTENV_FILE` prefixes HAVE to be there. This needs a recent `posthog-cli` (older ones silently ignore `POSTHOG_CLI_DOTENV_FILE`); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++- **Android (Gradle)** Android uploads **ProGuard/R8 mapping files**, not source maps. Apply the `com.posthog.android` Gradle plugin on the **app module's** `build.gradle(.kts)` (never the root project), per the reference — the plugin hooks the build and uploads automatically, do not hand-roll a `posthog-cli` step. Gotchas: ++ 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. ++ 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++ 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. ++- **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. ++- **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. ++ - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. ++ - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. ++ - **iOS** Follow the **iOS (Xcode)** bullet above, on the **Runner** target in `ios/Runner.xcworkspace`. Flutter is always CocoaPods: `${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh`. ++ ++ Set `captureNativeExceptions = true` in `PostHogConfig.errorTrackingConfig` — it defaults to `false`, and while it's off the native SDKs capture nothing to symbolicate. ++ ++### Make credentials available at build time ++ ++The upload credentials must be readable **by the build pipeline at build time**, not merely present in a `.env` file. Whether `.env` is auto-loaded depends on the technology. ++ ++#### Tips ++- **Auto-loads `.env`**: Next.js, Nuxt and similar frameworks read `.env` into the build for you — nothing extra to do. ++- **Vite is a partial exception**: it auto-loads `.env` into `import.meta.env` for client code (only `VITE_`-prefixed vars), but does **not** put vars in `process.env` for your config to read. The upload credentials (`POSTHOG_*`, not `VITE_`-prefixed) are read when the plugin is constructed, so load them yourself — see the Vite example below. ++- **Does NOT auto-load `.env`**: Rollup, plain webpack, and plain Node scripts. Load it explicitly — add `dotenv` (`require('dotenv').config()`, or `import 'dotenv/config'` for ESM) at the top of the bundler/config file. ++- **Separate-process gotcha**: if `posthog-cli sourcemap process` runs as its own `package.json` step (after the bundler), the CLI call is a **separate child process** and will *not* see env vars a loader set inside the bundler config. Point the CLI at the file directly: `posthog-cli --dotenv-file sourcemap process …` (the flag goes before the subcommand). ++- **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) ++- **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. ++- **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. ++- **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: ++ - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). ++ - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. ++ - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. ++ ++#### Examples ++- **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. ++- **Vite** Export `vite.config` as a function and merge `loadEnv` into `process.env` so the config (and the PostHog plugin) can read the upload credentials. Pass `''` as the third arg so non-`VITE_` vars like `POSTHOG_API_KEY` are included — the default `'VITE_'` prefix skips them: ++ ```ts ++ import { defineConfig, loadEnv } from 'vite'; ++ ++ export default ({ mode }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') }; ++ // process.env.POSTHOG_API_KEY is now readable by the plugins below ++ return defineConfig({ ++ plugins: [/* … posthog source map plugin … */], ++ }); ++ }; ++ ``` ++- **Rollup / webpack / plain Node** Add `import 'dotenv/config'` (or `require('dotenv').config()`) at the top of the config/entry file so the loader runs before the build reads the vars. ++- **Standalone posthog-cli step** Pass `--dotenv-file .env` to the `process` invocation so it can authenticate: ++ ```json ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name my-app" ++ ``` ++- **iOS (Xcode / posthog-cli)** A gitignored `.env` next to the `.xcodeproj` — the Run Script invocation's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix hands it to posthog-cli. No Xcode project wiring beyond the Run Script phase. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Android (Gradle / posthog-cli)** A gitignored `.env` at the Gradle project root, bridged into the upload tasks in the **app module's** `build.gradle.kts`: ++ ```kotlin ++ import com.posthog.android.PostHogCliExecTask ++ import java.util.Properties ++ ++ val postHogEnv = Properties().apply { ++ val envFile = rootProject.file(".env") ++ if (envFile.exists()) envFile.inputStream().use { load(it) } ++ } ++ ++ tasks.withType().configureEach { ++ postHogEnv.getProperty("POSTHOG_CLI_API_KEY")?.let { postHogApiKey.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_PROJECT_ID")?.let { postHogProjectId.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_HOST")?.let { postHogHost.set(it) } ++ } ++ ``` ++ (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. ++ ++### Write credentials to the env file ++ ++Write the personal API key and project identifiers into the env file your build reads. Reuse the file the project already uses — don't introduce a second one. ++ ++#### Tips ++- Picking the file: if an env file already contains PostHog vars (`POSTHOG_*` / `NEXT_PUBLIC_POSTHOG_*`), use that one. Otherwise, if exactly one env file exists use it; if several exist prefer `.env`. Only create a new file when none exists. ++- Variable names depend on which uploader you wired: ++ - `posthog-cli` direct upload → `POSTHOG_CLI_API_KEY`, `POSTHOG_CLI_PROJECT_ID`, `POSTHOG_CLI_HOST` ++ - bundler-plugin variants → `POSTHOG_API_KEY`, `POSTHOG_PROJECT_ID`, `POSTHOG_HOST` ++- Set the `*_HOST` var when you're not on US Cloud's default (e.g. EU Cloud or self-hosted); setting it explicitly always is safe. Follow the reference for the variant. ++- In CI/CD, set the same vars as secrets — never commit the key. ++ ++### Identify the build and run commands ++ ++Resolve two concrete commands for this project: the production **build** command (the one that uploads source maps) and the **run** command that launches the built app (so a test error can be triggered against the real artifact). ++ ++#### Tips ++- Resolve real commands from the project's actual scripts/config — substitute the correct package manager. Never leave a generic "start the app". ++- When a build artifact is involved, prefer the command that serves the *production* build over the dev server. ++ ++#### Examples ++- **Next.js** Build: `npm run build` (`next build`). Run: `npm run start` (`next start`). ++- **Vite** Build: `npm run build`. Run: `npm run preview`. ++- **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). ++- **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). ++- **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). ++- **Flutter** One pair per platform you wired: ++ - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. ++ - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. ++ - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. ++ ++### Set up CI for automatic uploads ++ ++Source maps are only uploaded when the **production build** runs, so the environment that builds and deploys your app needs the same upload credentials you put in the env file. The whole job is: **find where the production build command actually runs, then make the upload credentials reachable at that exact spot.** **Only ever edit CI/deploy files that already exist — never create a new workflow, pipeline, or deploy file.** Wiring credentials means modifying the build/deploy config this project already has; it is never license to author new CI. The build is where maps inject + upload, and env does **not** automatically cross three boundaries — into a Docker build, into a nested/composite action, or into an SSH session. So trace the deploy path before editing anything: ++ ++1. Is there a `Dockerfile`? If the build command runs inside it (`RUN `), the build happens in that image's **build stage**. ++2. Is there a workflow under `.github/workflows/`? Open it and find the step that triggers the build, then follow it to where the build truly executes — it may be: ++ - an inline build step (`run: npm run build`) on the runner, ++ - a `docker build` / `docker/build-push-action` step (build runs in the image), ++ - a `uses: ./.github/actions/...` **local composite action** — open that `action.yml`; the real build step is one layer down, ++ - an `ssh`/deploy step (e.g. `appleboy/ssh-action`) whose `script:` runs the build **on a remote server**. ++3. Any other CI config in the repo (`.gitlab-ci.yml`, `.circleci/config.yml`, `Jenkinsfile`, `bitbucket-pipelines.yml`, `azure-pipelines.yml`, …)? Open it and find the job/stage that runs the production build. The principle is identical; apply it with your working knowledge of that provider — the examples below show the pattern to mirror. ++4. No `Dockerfile`, no CI config, no build step you can trace? Don't guess — tell the user where the creds need to be (see "Untraceable setup" under Examples). ++ ++#### Tips ++- **A deploy file for another package is not license to author one for this one.** In a monorepo especially, finding a workflow that deploys a *sibling* package (e.g. a `deploy-backend.yml`, or a `Dockerfile`/pipeline for another app) does **not** mean you should create a matching `deploy-frontend.yml` (or any new CI file) for the project you're instrumenting. Wire credentials only into the existing file that builds *this* project. If this project has no build/deploy config you can open and edit, it is untraceable: make no CI changes and hand the requirement to the user (see "Untraceable setup") — do **not** invent one. ++- Reuse the **exact variable names** from "Write credentials to the env file" — the build reads the same names locally and in CI. (`POSTHOG_CLI_*` for direct `posthog-cli`; `POSTHOG_*` for bundler-plugin uploaders.) ++- **In CI, credentials travel as environment variables — never as a file.** Do not materialize a `.env` on the runner (e.g. `printf … > .env` before the build), and never copy or un-ignore one into a Docker image: it's redundant, and a secrets file on disk can leak into artifacts, caches, or image layers. A build script that passes `--dotenv-file .env` to `posthog-cli` works unchanged in CI even though `.env` doesn't exist there: real environment variables take precedence over the file, and a missing file is skipped with a warning. ++- **Never commit secret values.** Reference credentials by name only: Docker `ARG`/`ENV` or BuildKit secret ids, `${{ secrets.* }}` in GitHub Actions. The personal API key stays out of version control. ++- Layers stack — a workflow can call a composite action that runs `docker build` against a Dockerfile. Wire **every** layer the credentials must pass through, from the outer `${{ secrets.* }}` reference down to the `ARG`/`ENV` in the build stage. ++- **Multi-stage Dockerfiles:** put the `ARG`/`ENV` in the **build stage** (where the build command runs), never the runtime stage. That's both correct (the build needs them) and safer (the creds don't get baked into the shipped image). ++- **Single-stage Dockerfiles:** with no separate build stage, `ARG`/`ENV` would bake the API key into the shipped image (`docker inspect` reveals `ENV`; `docker history` can reveal build args). Mount the key as a **BuildKit secret** on the build `RUN` instead — it exists for that command only and is never written to a layer (see the single-stage example). Plain `ARG`/`ENV` stays fine for the non-secret project ID and host. ++- **Composite / reusable actions can't read `secrets`.** Inside a `.github/actions/*/action.yml` only `${{ inputs.* }}` is available. Add an `inputs:` entry per credential, reference `${{ inputs.* }}` there, and pass `${{ secrets.* }}` from the calling workflow's `with:` block. ++- **Build over SSH:** the runner's env doesn't reach the remote box. Set the vars inline immediately before the build command inside the `script:`. `${{ secrets.* }}` is substituted by Actions *before* the script is sent, so the value travels with the script. ++- **The worked examples are exemplars, not an allowlist.** For any provider not shown (GitLab CI, CircleCI, Jenkins, Bitbucket, Azure Pipelines, …), apply the same principle with your knowledge of that provider: find the job that runs the production build, expose the credentials there via the provider's native secret mechanism (GitLab project CI/CD variables, CircleCI project env vars / contexts, Jenkins credentials + `withCredentials`, …), and cross the same boundaries the same way — Docker builds still need `--build-arg`, SSH sessions still need inline vars. ++- **Make only the edits the provider actually needs.** Some providers inject project-level variables straight into every job's environment — GitLab CI/CD variables work this way — so an inline build step may need **no functional pipeline change at all**. When that's the conclusion, still add a short comment on the build job naming the required variables and where to create them (see the GitLab example) — the requirement must be visible in the repo, not only in your hand-off — and tell the user exactly which variables to create and where. ++- You can't create CI secrets. Whenever the pipeline reads a credential, tell the user where to add it before their next deploy — GitHub: **Settings → Secrets and variables → Actions**; GitLab: **Settings → CI/CD → Variables**; other providers: their equivalent secret store. The pipeline can't read a secret that doesn't exist yet. ++ ++#### Examples ++- **Dockerfile build stage (e.g. `Dockerfile`, no CI)** Declare the credentials as build args and promote them to env vars *before* the build `RUN`, in the build stage: ++ ```dockerfile ++ FROM node:22-slim AS build ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_API_KEY ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_API_KEY=$POSTHOG_CLI_API_KEY \ ++ POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN npm run build # now sees the upload credentials ++ ``` ++ With no CI wiring the image, tell the user to pass them when they build: `docker build --build-arg POSTHOG_CLI_API_KEY=… --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .` ++- **Single-stage Dockerfile (BuildKit secret)** When build and runtime share one stage, pass the API key as a BuildKit secret so it never lands in the image; keep `ARG`/`ENV` for the non-secret project ID and host: ++ ```dockerfile ++ # syntax=docker/dockerfile:1 ++ FROM node:22-slim ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY \ ++ npm run build ++ ``` ++ Build with `docker build --secret id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .`. In `docker/build-push-action`, pass the key through the `secrets:` input (`POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }}`) instead of `build-args:`. The `env=` attribute on `--mount` needs a current BuildKit — keep the `# syntax=docker/dockerfile:1` line; on engines too old for it, read the file form instead: `RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY POSTHOG_CLI_API_KEY=$(cat /run/secrets/POSTHOG_CLI_API_KEY) npm run build`. ++- **GitHub Actions — inline build step** Build runs on the runner; expose the creds with `env:` on that step: ++ ```yaml ++ - name: Build ++ run: npm run build ++ env: ++ POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — `docker build` / `docker/build-push-action`** Add the `ARG`/`ENV` to the Dockerfile build stage (above), then forward the creds as build args. Raw `docker build` takes `--build-arg`; `docker/build-push-action` takes a multi-line `build-args:` input — **merge into the existing `with:` block, don't add a second step**: ++ ```yaml ++ - name: Build and push image ++ uses: docker/build-push-action@v6 ++ with: ++ context: . ++ file: Dockerfile ++ push: true ++ tags: ${{ steps.meta.outputs.tags }} ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID=${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST=${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — nested/composite action** When the workflow delegates the build with `uses: ./.github/actions/build-and-push`, the `build-push-action` lives in that action's `action.yml`, which can't see `secrets`. Thread them through as inputs. In `.github/actions/build-and-push/action.yml`: ++ ```yaml ++ inputs: ++ posthog-cli-api-key: ++ required: true ++ posthog-cli-project-id: ++ required: true ++ posthog-cli-host: ++ required: true ++ runs: ++ using: composite ++ steps: ++ - uses: docker/build-push-action@v6 ++ with: ++ # ...existing context/file/push/tags... ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ inputs.posthog-cli-api-key }} ++ POSTHOG_CLI_PROJECT_ID=${{ inputs.posthog-cli-project-id }} ++ POSTHOG_CLI_HOST=${{ inputs.posthog-cli-host }} ++ ``` ++ Then pass the secrets from the calling workflow's `with:` block: ++ ```yaml ++ - uses: ./.github/actions/build-and-push ++ with: ++ # ...existing inputs... ++ posthog-cli-api-key: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ posthog-cli-project-id: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ posthog-cli-host: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — build over SSH** When a step SSHes into a server and runs the build there (e.g. `appleboy/ssh-action` with `git pull && npm run build`), set the vars inline right before the build command inside the `script:` — mirror however the script already passes runtime vars: ++ ```yaml ++ - uses: appleboy/ssh-action@v1 ++ with: ++ host: ${{ secrets.DEPLOY_HOST }} ++ # ... ++ script: | ++ cd /srv/app && git pull --ff-only origin main && npm ci ++ POSTHOG_CLI_API_KEY="${{ secrets.POSTHOG_CLI_API_KEY }}" \ ++ POSTHOG_CLI_PROJECT_ID="${{ secrets.POSTHOG_CLI_PROJECT_ID }}" \ ++ POSTHOG_CLI_HOST="${{ secrets.POSTHOG_CLI_HOST }}" \ ++ npm run build ++ ``` ++- **GitLab CI (`.gitlab-ci.yml`)** Project CI/CD variables are injected into every job's environment automatically, so a job that runs the build inline (`script: - npm run build`) needs **no functional YAML change** — no `variables:` block, and do NOT add a script line that writes the variables into a `.env` file (`printf … > .env`, `echo … >> .env`, etc.); the build already sees them as environment variables, which take precedence over any dotenv file. DO leave a comment on the build job so the requirement is visible in the repo, not only in your hand-off: ++ ```yaml ++ build: ++ stage: build ++ # PostHog source map upload: this job needs POSTHOG_CLI_API_KEY, ++ # POSTHOG_CLI_PROJECT_ID and POSTHOG_CLI_HOST available as CI/CD ++ # variables (Settings → CI/CD → Variables); GitLab injects them into ++ # the job automatically. Mark them Masked — but Protected only if this ++ # job runs exclusively on protected branches, otherwise feature-branch ++ # builds fail with missing credentials. ++ script: ++ - npm ci ++ - npm run build ++ ``` ++ Then tell the user to add those variables in **Settings → CI/CD → Variables** and the next pipeline picks them up. Edits beyond the comment are only needed when a boundary is crossed: a job that runs `docker build` must forward them (`--build-arg POSTHOG_CLI_API_KEY="$POSTHOG_CLI_API_KEY" …`) into the Dockerfile's build stage (see the Dockerfile example), and a job that builds over SSH must set them inline before the remote build command, exactly like the SSH example above. ++- **Other CI providers (CircleCI, Jenkins, Bitbucket, Azure Pipelines, …)** Same recipe, provider-native mechanics: open the pipeline config, find the job that runs the production build, expose the credentials to that job via the provider's secret store, and thread them through any Docker/SSH boundary just like the examples above. Reference credentials by name only, then tell the user each secret to create and exactly where in the provider's UI it goes. ++- **Untraceable setup** No `Dockerfile`, no CI config, and no build step you can trace: make no CI changes — do **not** author a new workflow, pipeline, or deploy file to fill the gap. Tell the user that wherever their production build command runs, it must have the upload credentials (`POSTHOG_CLI_*` / `POSTHOG_*`) available as environment variables, or maps won't upload on deploy. If part of the path is still recognisable — e.g. a `Dockerfile` built by an unfamiliar CI — wire the layers you do recognise and tell the user exactly what the remaining layer must pass in (e.g. the `--build-arg` flags). ++ ++### Associate the release with a git commit ++ ++`posthog-cli` links the release to a **git commit, branch and repo** so Error Tracking can show which deploy an error came from. It auto-detects that from the CI's git env vars or a local `.git` directory — you never touch the CLI invocation itself (it's usually baked into `npm run build` or a bundler plugin), you just make the git context available in the build environment. A `docker build` is where this breaks: it sees **neither** the env vars nor `.git` (the same boundary credentials hit), so the release ends up linked to nothing unless you forward the vars in. ++ ++#### Tips ++- **Forward GitHub's git env vars into the Docker build** the same way you forwarded credentials. Declare each as an `ARG` **and** promote it to `ENV` — `ARG` alone isn't visible to the CLI's env lookup. That's all auto-detection needs; no CLI flags, no `.git`. ++ ++#### Examples ++- **GitHub Actions → docker build** Forward GitHub's git vars into the build stage and the CLI auto-detects branch + repo + commit: ++ ```yaml ++ build-args: | ++ GITHUB_ACTIONS=true ++ GITHUB_SHA=${{ github.sha }} ++ GITHUB_REF_NAME=${{ github.ref_name }} ++ GITHUB_REPOSITORY=${{ github.repository }} ++ GITHUB_SERVER_URL=${{ github.server_url }} ++ ``` ++ Then in the build stage, declare each as `ARG` and re-export it as `ENV` before the build runs. ++- **Inline CI build (no Docker)** GitHub Actions already sets these vars on the runner, so auto-detection just works — nothing to pass. ++ ++### Test the local setup ++ ++Optionally add a temporary, clearly-labeled affordance that captures one test exception, so you can confirm errors arrive in Error Tracking with a source-resolved stack trace after the next production build. Always remove it afterwards. ++ ++#### Tips ++- The handler must call the SDK's exception-capture method **directly** — do **not** `throw`. Throwing depends on the global error handler and shows a dev overlay; a direct capture is deterministic across platforms. ++- Pass a single Error (or platform-equivalent throwable). No custom message beyond the Error, no extra properties, no second argument — the Error's stack trace is what gets resolved. ++- Use distinctive copy on the trigger (button label / route path) so the resulting event is easy to find in the UI. ++- Read any file before editing it and capture its exact contents; after testing, restore every file the affordance touched — the affordance only, leave the upload and credential wiring in place — and re-read to confirm nothing is left behind. Never leave the affordance in place — even if the test "didn't work", revert first. ++- The upload only happens on the *production build*: build, run, trigger the error, then confirm the stack trace in Error Tracking points at real source files, not minified bundle paths. ++ ++#### Examples ++- **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: ++ ```kotlin ++ import com.posthog.PostHog ++ ++ PostHog.captureException(Throwable("PostHog source maps test")) ++ ``` ++ Test flow — the upload only runs on the **minified release variant**: `./gradlew installRelease` (or Android Studio ▸ Build Variants ▸ release, then Run), launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **iOS (Swift)** `Button` on the root view (SwiftUI) or `UIButton` on the root view controller (UIKit), handler: ++ ```swift ++ do { ++ throw NSError(domain: "PostHogSourceMapTest", code: 1, ++ userInfo: [NSLocalizedDescriptionKey: "Source map upload test error"]) ++ } catch { ++ PostHogSDK.shared.captureException(error) ++ } ++ ``` ++ (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. ++- **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. ++ ++### Verify and hand off ++ ++Confirm the upload landed and report what changed. ++ ++#### Tips ++- Source maps upload during the **production build** — the build must actually run for a symbol set to appear. ++- Verify in PostHog Error Tracking settings on the **Symbol sets** page: a new symbol set should appear after the build completes. ++- When handing off, list the files you edited (paths only), the env-var **key** names you set (never values), whether a test affordance was added and reverted, and the exact build command to run. ++- If you wired CI, list the pipeline files you changed (`Dockerfile`, workflow, pipeline config) and spell out every manual follow-up — e.g. the secrets the user must add in their CI provider's settings before their next deploy, or the note that their build path couldn't be traced. ++ ++## General tips ++- The reference files for Node.js are authoritative — if this page and a reference disagree on an API, follow the reference. ++- Two different keys, two different jobs: a **personal API key** uploads maps at build time; the **public project key** powers the SDK at runtime. Don't swap them. ++- Keep build artifacts and uploaded maps in sync — every deploy should inject + upload within the same build so stack traces always resolve. ++- Uploaded maps live in PostHog and never need to be served publicly. ++- Detect the project's package manager before installing any dependency. ++- Read a file (and note its exact contents) immediately before editing it — essential for any temporary test code you'll revert afterwards. ++ ++## Framework guidelines ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- posthog-node is the Node.js server-side SDK package name; posthog-js is browser-only, so use posthog-node on the server instead ++- Include enableExceptionAutocapture: true in the PostHog constructor options ++- Add posthog.capture() calls in route handlers for meaningful user actions – every route that creates, updates, or deletes data should track an event with contextual properties ++- Add posthog.captureException(err, distinctId) in the application's error handler (e.g., Express error middleware, Fastify setErrorHandler, Koa app.on('error')) ++- The SDK batches events and flushes asynchronously. await flush() or await shutdown() before letting that process exit. If unsure, set flushAt 1 and flushInterval 0. ++- `posthog.capture()` enqueues synchronously and returns; the batched HTTP send happens afterwards. Treat every per-request handler as short-lived even when the framework feels like a server: Next.js / Nuxt / SvelteKit / Remix route handlers, serverless and edge functions, and Lambda are torn down per invocation before the send runs. Create the client with flushAt 1 and flushInterval 0, then await the send before returning. Always use `await posthog.flush()` for a shared/singleton client, `await posthog.shutdown()` for a per-request client. Never skip the awaited flush or risk the enqueued event being silently dropped. ++- Reverse proxy is NOT needed for server-side Node.js – only client-side JavaScript needs a proxy to avoid ad blockers ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md b/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md +new file mode 100644 +index 0000000..11206d5 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md +@@ -0,0 +1,15 @@ ++# Framework rules ++ ++Follow these when integrating PostHog into this framework. ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- posthog-node is the Node.js server-side SDK package name; posthog-js is browser-only, so use posthog-node on the server instead ++- Include enableExceptionAutocapture: true in the PostHog constructor options ++- Add posthog.capture() calls in route handlers for meaningful user actions – every route that creates, updates, or deletes data should track an event with contextual properties ++- Add posthog.captureException(err, distinctId) in the application's error handler (e.g., Express error middleware, Fastify setErrorHandler, Koa app.on('error')) ++- The SDK batches events and flushes asynchronously. await flush() or await shutdown() before letting that process exit. If unsure, set flushAt 1 and flushInterval 0. ++- `posthog.capture()` enqueues synchronously and returns; the batched HTTP send happens afterwards. Treat every per-request handler as short-lived even when the framework feels like a server: Next.js / Nuxt / SvelteKit / Remix route handlers, serverless and edge functions, and Lambda are torn down per invocation before the send runs. Create the client with flushAt 1 and flushInterval 0, then await the send before returning. Always use `await posthog.flush()` for a shared/singleton client, `await posthog.shutdown()` for a per-request client. Never skip the awaited flush or risk the enqueued event being silently dropped. ++- Reverse proxy is NOT needed for server-side Node.js – only client-side JavaScript needs a proxy to avoid ad blockers ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md +new file mode 100644 +index 0000000..51bfb01 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md +@@ -0,0 +1,150 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ ++# Upload source maps with CLI - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Download CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++ If you already keep your project's configuration in a dotenv-style file, you can load these variables from it with the `--dotenv-file` option instead of exporting them: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --dotenv-file .env sourcemap upload --directory ./path/to/assets ++ ``` ++ ++3. 3 ++ ++ ## Inject ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++ You can verify that the metadata has been injected by checking for the `//# chunkId=...` comment in the minified code. ++ ++4. 4 ++ ++ ## Upload ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++5. 5 ++ ++ ## Serve injected assets ++ ++ Required ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++7. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/node.md b/.claude/skills/error-tracking-upload-source-maps-node/references/node.md +new file mode 100644 +index 0000000..16626cc +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/node.md +@@ -0,0 +1,166 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Node.js - Docs ++ ++Copy page ++ ++# Upload source maps for Node.js - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Install the PostHog CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate the PostHog CLI ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++3. 3 ++ ++ ## Output source maps for Node.js ++ ++ Required ++ ++ *Your goal in this step: Configure your build to generate source maps.* ++ ++ If you serve minified bundles in production, PostHog requires source maps to generate accurate stack traces. Here are instructions to enable source map generation for popular build tools: ++ ++ | Build Tool | Documentation | ++ | --- | --- | ++ | Vite | [Source Map Configuration](https://v3.vitejs.dev/config/build-options.html#build-sourcemap) | ++ | webpack | [Source Map Configuration](https://webpack.js.org/configuration/devtool/) | ++ | Rollup | [Source Map Options](https://rollupjs.org/configuration-options/#output-sourcemap) | ++ ++ For other build tools, consult their documentation to enable source maps. ++ ++4. 4 ++ ++ ## Inject source map ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++5. ## Verify source map injection ++ ++ Checkpoint ++ ++ Confirm that the served files are injected with the correct source map comment in production in dev tools: ++ ++ JavaScript ++ ++ PostHog AI ++ ++ ```javascript ++ //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c ++ ``` ++ ++6. 5 ++ ++ ## Upload source map ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++ #### Serve injected assets ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. We suggest you upload source maps right after your production build in CI. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++8. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md +new file mode 100644 +index 0000000..b6ae318 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md +@@ -0,0 +1,67 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ ++# Upload source maps - Docs ++ ++If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. ++ ++If your source maps are not publicly hosted, you will need to upload them during your build process to see unminified code in your stack traces. ++ ++## AI wizard ++ ++If you're using a JavaScript or TypeScript framework, set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++Otherwise, choose your platform below for manual instructions. ++ ++## Platforms ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)Web](/docs/error-tracking/upload-source-maps/web.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nextjs.svg)Next.js](/docs/error-tracking/upload-source-maps/nextjs.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/nodejs.svg)Node.js](/docs/error-tracking/upload-source-maps/node.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React](/docs/error-tracking/upload-source-maps/react.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/docs/integrate/frameworks/angular.svg)Angular](/docs/error-tracking/upload-source-maps/angular.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nuxt.svg)Nuxt](/docs/error-tracking/upload-source-maps/nuxt.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React Native](/docs/error-tracking/upload-source-maps/react-native.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Android_robot_bec2fb7318.svg)Android](/docs/error-tracking/upload-mappings/android.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/webpack_3fc774b5a5.svg)Webpack](/docs/error-tracking/upload-source-maps/webpack.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Vitejs_logo_98ffe5d5ee.svg)Vite](/docs/error-tracking/upload-source-maps/vite.md) ++ ++- [CLI](/docs/error-tracking/upload-source-maps/cli.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/package.json b/package.json +index d9035ab..a12b555 100644 +--- a/package.json ++++ b/package.json +@@ -4,7 +4,7 @@ + "private": true, + "type": "module", + "scripts": { +- "build": "tsc", ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name node-raw", + "start": "node --env-file=.env dist/index.js" + }, + "dependencies": { +diff --git a/tsconfig.json b/tsconfig.json +index 2659d22..ded0133 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -6,7 +6,9 @@ + "outDir": "dist", + "rootDir": "src", + "strict": true, +- "skipLibCheck": true ++ "skipLibCheck": true, ++ "sourceMap": true, ++ "inlineSources": true + }, + "include": ["src"] + } diff --git a/results/source-maps-sol-medium/node-raw__anthropic/result.json b/results/source-maps-sol-medium/node-raw__anthropic/result.json new file mode 100644 index 000000000..8971a1ec4 --- /dev/null +++ b/results/source-maps-sol-medium/node-raw__anthropic/result.json @@ -0,0 +1,23 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "posthog-node" + ], + "envFile": "/tmp/sm-run-node-raw__anthropic/.env", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file diff --git a/results/source-maps-sol-medium/node-raw__sol-medium/app.diff b/results/source-maps-sol-medium/node-raw__sol-medium/app.diff new file mode 100644 index 000000000..1fba603f1 --- /dev/null +++ b/results/source-maps-sol-medium/node-raw__sol-medium/app.diff @@ -0,0 +1,1007 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/.posthog-wizard b/.claude/skills/error-tracking-upload-source-maps-node/.posthog-wizard +new file mode 100644 +index 0000000..e69de29 +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md +new file mode 100644 +index 0000000..f3a7670 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/SKILL.md +@@ -0,0 +1,415 @@ ++--- ++name: error-tracking-upload-source-maps-node ++description: Upload source maps to PostHog Error Tracking for Node.js ++metadata: ++ author: PostHog ++ version: 1.49.1 ++--- ++ ++# Upload source maps to PostHog for Node.js ++ ++This skill helps you upload source maps (or platform debug symbols) so PostHog Error Tracking can resolve minified stack traces back to your original source. ++ ++## Reference files ++ ++- `references/node.md` - Upload source maps for Node.js - docs ++- `references/upload-source-maps.md` - Upload source maps - docs ++- `references/cli.md` - Upload source maps with cli - docs ++- `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow ++ ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. ++ ++## Steps ++ ++The stages of wiring up source map upload, in order. Each step has a short overview, gotchas under **Tips**, and per-technology notes under **Examples**. The reference files above are the source of truth for the exact, per-framework API — when this page and a reference disagree, follow the reference for Node.js. ++ ++### Get a personal API key ++ ++Source map upload authenticates with a **personal API key**, not the public project API key the SDK uses at runtime. The key needs error-tracking write access; the quickest path is the "Source map upload" preset on PostHog's personal API keys settings page. ++ ++#### Tips ++- The public project key (the one in your SDK `init`) will **not** work for uploads — it has no write scope for symbol sets. ++- Never hardcode the key in source. It belongs in an environment variable read at build time (see "Write credentials to the env file"). ++- Keys can't be minted programmatically — create them by hand in PostHog settings, then store the value as a secret. ++ ++### Apply build-config changes ++ ++Wire source map generation, chunk-ID injection, and upload into your **production build** so every deploy ships matching maps. Depending on the platform this is either a build/bundler plugin, or a `posthog-cli sourcemap process` step run after the build (it injects chunk IDs and uploads in one pass). Follow the Node.js reference for the exact wiring. ++ ++#### Tips ++- If you wire `posthog-cli` directly (no framework or bundler plugin), generating the maps is **your** responsibility — the CLI only injects chunk IDs into, and uploads, maps your build already produced. Two things must be true before `posthog-cli sourcemap process` works: ++ - Source maps are emitted next to your output bundles (e.g. `.js.map` files). ++ - The maps include `sourcesContent` (the original source embedded inside the map). Without it PostHog has the line/column mappings but not the code, so traces can't be fully resolved. ++- **Inject before deploy**: the *injected* bundles must be the ones shipped to production. Bundles missing the `//# chunkId=…` comment can't be matched to uploaded maps. ++- Wire injection + upload into the build itself (plugin, post-build script, or CI step) — manual uploads drift from deployed code. ++- **Don't ship source maps publicly**: omit `.map` files from the deployed artifact, or use hidden source maps. Uploaded maps live in PostHog, not on your origin. ++- **Link each release to its commit.** The CLI auto-detects the commit from the CI's git env vars — see "Associate the release with a git commit" for making those reachable in Docker/CI builds. ++ ++#### Examples ++- **Node / tsc** Emit maps with embedded sources by setting both in `tsconfig.json`: `"sourceMap": true` and `"inlineSources": true`. Then run `posthog-cli sourcemap process` against the build output dir as a post-build step — it injects chunk IDs and uploads in one pass, and needs the upload credentials (see "Make credentials available at build time"). ++- **Vite / Webpack / Rollup** Prefer the bundler plugin from the reference over hand-rolling the CLI — it injects and uploads in one pass. Make sure the bundler is configured to emit source maps. ++- **iOS (Xcode)** iOS uploads **dSYM debug symbols**, not source maps. Required target changes: ++ 1. `DEBUG_INFORMATION_FORMAT = dwarf-with-dsym` for Release. ++ 2. `ENABLE_USER_SCRIPT_SANDBOXING = NO`. ++ 3. A Run Script phase, ordered last, with `$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)` in its Input Files, calling the SDK's bundled script — do not hand-roll the upload: ++ - SPM: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/posthog-ios/build-tools/upload-symbols.sh"` ++ - CocoaPods: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh"` ++ Copy the invocation verbatim — the `POSTHOG_INCLUDE_SOURCE=1` and `POSTHOG_CLI_DOTENV_FILE` prefixes HAVE to be there. This needs a recent `posthog-cli` (older ones silently ignore `POSTHOG_CLI_DOTENV_FILE`); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++- **Android (Gradle)** Android uploads **ProGuard/R8 mapping files**, not source maps. Apply the `com.posthog.android` Gradle plugin on the **app module's** `build.gradle(.kts)` (never the root project), per the reference — the plugin hooks the build and uploads automatically, do not hand-roll a `posthog-cli` step. Gotchas: ++ 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. ++ 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++ 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. ++- **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. ++- **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. ++ - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. ++ - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. ++ - **iOS** Follow the **iOS (Xcode)** bullet above, on the **Runner** target in `ios/Runner.xcworkspace`. Flutter is always CocoaPods: `${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh`. ++ ++ Set `captureNativeExceptions = true` in `PostHogConfig.errorTrackingConfig` — it defaults to `false`, and while it's off the native SDKs capture nothing to symbolicate. ++ ++### Make credentials available at build time ++ ++The upload credentials must be readable **by the build pipeline at build time**, not merely present in a `.env` file. Whether `.env` is auto-loaded depends on the technology. ++ ++#### Tips ++- **Auto-loads `.env`**: Next.js, Nuxt and similar frameworks read `.env` into the build for you — nothing extra to do. ++- **Vite is a partial exception**: it auto-loads `.env` into `import.meta.env` for client code (only `VITE_`-prefixed vars), but does **not** put vars in `process.env` for your config to read. The upload credentials (`POSTHOG_*`, not `VITE_`-prefixed) are read when the plugin is constructed, so load them yourself — see the Vite example below. ++- **Does NOT auto-load `.env`**: Rollup, plain webpack, and plain Node scripts. Load it explicitly — add `dotenv` (`require('dotenv').config()`, or `import 'dotenv/config'` for ESM) at the top of the bundler/config file. ++- **Separate-process gotcha**: if `posthog-cli sourcemap process` runs as its own `package.json` step (after the bundler), the CLI call is a **separate child process** and will *not* see env vars a loader set inside the bundler config. Point the CLI at the file directly: `posthog-cli --dotenv-file sourcemap process …` (the flag goes before the subcommand). ++- **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) ++- **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. ++- **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. ++- **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: ++ - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). ++ - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. ++ - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. ++ ++#### Examples ++- **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. ++- **Vite** Export `vite.config` as a function and merge `loadEnv` into `process.env` so the config (and the PostHog plugin) can read the upload credentials. Pass `''` as the third arg so non-`VITE_` vars like `POSTHOG_API_KEY` are included — the default `'VITE_'` prefix skips them: ++ ```ts ++ import { defineConfig, loadEnv } from 'vite'; ++ ++ export default ({ mode }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') }; ++ // process.env.POSTHOG_API_KEY is now readable by the plugins below ++ return defineConfig({ ++ plugins: [/* … posthog source map plugin … */], ++ }); ++ }; ++ ``` ++- **Rollup / webpack / plain Node** Add `import 'dotenv/config'` (or `require('dotenv').config()`) at the top of the config/entry file so the loader runs before the build reads the vars. ++- **Standalone posthog-cli step** Pass `--dotenv-file .env` to the `process` invocation so it can authenticate: ++ ```json ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name my-app" ++ ``` ++- **iOS (Xcode / posthog-cli)** A gitignored `.env` next to the `.xcodeproj` — the Run Script invocation's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix hands it to posthog-cli. No Xcode project wiring beyond the Run Script phase. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Android (Gradle / posthog-cli)** A gitignored `.env` at the Gradle project root, bridged into the upload tasks in the **app module's** `build.gradle.kts`: ++ ```kotlin ++ import com.posthog.android.PostHogCliExecTask ++ import java.util.Properties ++ ++ val postHogEnv = Properties().apply { ++ val envFile = rootProject.file(".env") ++ if (envFile.exists()) envFile.inputStream().use { load(it) } ++ } ++ ++ tasks.withType().configureEach { ++ postHogEnv.getProperty("POSTHOG_CLI_API_KEY")?.let { postHogApiKey.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_PROJECT_ID")?.let { postHogProjectId.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_HOST")?.let { postHogHost.set(it) } ++ } ++ ``` ++ (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. ++ ++### Write credentials to the env file ++ ++Write the personal API key and project identifiers into the env file your build reads. Reuse the file the project already uses — don't introduce a second one. ++ ++#### Tips ++- Picking the file: if an env file already contains PostHog vars (`POSTHOG_*` / `NEXT_PUBLIC_POSTHOG_*`), use that one. Otherwise, if exactly one env file exists use it; if several exist prefer `.env`. Only create a new file when none exists. ++- Variable names depend on which uploader you wired: ++ - `posthog-cli` direct upload → `POSTHOG_CLI_API_KEY`, `POSTHOG_CLI_PROJECT_ID`, `POSTHOG_CLI_HOST` ++ - bundler-plugin variants → `POSTHOG_API_KEY`, `POSTHOG_PROJECT_ID`, `POSTHOG_HOST` ++- Set the `*_HOST` var when you're not on US Cloud's default (e.g. EU Cloud or self-hosted); setting it explicitly always is safe. Follow the reference for the variant. ++- In CI/CD, set the same vars as secrets — never commit the key. ++ ++### Identify the build and run commands ++ ++Resolve two concrete commands for this project: the production **build** command (the one that uploads source maps) and the **run** command that launches the built app (so a test error can be triggered against the real artifact). ++ ++#### Tips ++- Resolve real commands from the project's actual scripts/config — substitute the correct package manager. Never leave a generic "start the app". ++- When a build artifact is involved, prefer the command that serves the *production* build over the dev server. ++ ++#### Examples ++- **Next.js** Build: `npm run build` (`next build`). Run: `npm run start` (`next start`). ++- **Vite** Build: `npm run build`. Run: `npm run preview`. ++- **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). ++- **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). ++- **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). ++- **Flutter** One pair per platform you wired: ++ - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. ++ - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. ++ - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. ++ ++### Set up CI for automatic uploads ++ ++Source maps are only uploaded when the **production build** runs, so the environment that builds and deploys your app needs the same upload credentials you put in the env file. The whole job is: **find where the production build command actually runs, then make the upload credentials reachable at that exact spot.** **Only ever edit CI/deploy files that already exist — never create a new workflow, pipeline, or deploy file.** Wiring credentials means modifying the build/deploy config this project already has; it is never license to author new CI. The build is where maps inject + upload, and env does **not** automatically cross three boundaries — into a Docker build, into a nested/composite action, or into an SSH session. So trace the deploy path before editing anything: ++ ++1. Is there a `Dockerfile`? If the build command runs inside it (`RUN `), the build happens in that image's **build stage**. ++2. Is there a workflow under `.github/workflows/`? Open it and find the step that triggers the build, then follow it to where the build truly executes — it may be: ++ - an inline build step (`run: npm run build`) on the runner, ++ - a `docker build` / `docker/build-push-action` step (build runs in the image), ++ - a `uses: ./.github/actions/...` **local composite action** — open that `action.yml`; the real build step is one layer down, ++ - an `ssh`/deploy step (e.g. `appleboy/ssh-action`) whose `script:` runs the build **on a remote server**. ++3. Any other CI config in the repo (`.gitlab-ci.yml`, `.circleci/config.yml`, `Jenkinsfile`, `bitbucket-pipelines.yml`, `azure-pipelines.yml`, …)? Open it and find the job/stage that runs the production build. The principle is identical; apply it with your working knowledge of that provider — the examples below show the pattern to mirror. ++4. No `Dockerfile`, no CI config, no build step you can trace? Don't guess — tell the user where the creds need to be (see "Untraceable setup" under Examples). ++ ++#### Tips ++- **A deploy file for another package is not license to author one for this one.** In a monorepo especially, finding a workflow that deploys a *sibling* package (e.g. a `deploy-backend.yml`, or a `Dockerfile`/pipeline for another app) does **not** mean you should create a matching `deploy-frontend.yml` (or any new CI file) for the project you're instrumenting. Wire credentials only into the existing file that builds *this* project. If this project has no build/deploy config you can open and edit, it is untraceable: make no CI changes and hand the requirement to the user (see "Untraceable setup") — do **not** invent one. ++- Reuse the **exact variable names** from "Write credentials to the env file" — the build reads the same names locally and in CI. (`POSTHOG_CLI_*` for direct `posthog-cli`; `POSTHOG_*` for bundler-plugin uploaders.) ++- **In CI, credentials travel as environment variables — never as a file.** Do not materialize a `.env` on the runner (e.g. `printf … > .env` before the build), and never copy or un-ignore one into a Docker image: it's redundant, and a secrets file on disk can leak into artifacts, caches, or image layers. A build script that passes `--dotenv-file .env` to `posthog-cli` works unchanged in CI even though `.env` doesn't exist there: real environment variables take precedence over the file, and a missing file is skipped with a warning. ++- **Never commit secret values.** Reference credentials by name only: Docker `ARG`/`ENV` or BuildKit secret ids, `${{ secrets.* }}` in GitHub Actions. The personal API key stays out of version control. ++- Layers stack — a workflow can call a composite action that runs `docker build` against a Dockerfile. Wire **every** layer the credentials must pass through, from the outer `${{ secrets.* }}` reference down to the `ARG`/`ENV` in the build stage. ++- **Multi-stage Dockerfiles:** put the `ARG`/`ENV` in the **build stage** (where the build command runs), never the runtime stage. That's both correct (the build needs them) and safer (the creds don't get baked into the shipped image). ++- **Single-stage Dockerfiles:** with no separate build stage, `ARG`/`ENV` would bake the API key into the shipped image (`docker inspect` reveals `ENV`; `docker history` can reveal build args). Mount the key as a **BuildKit secret** on the build `RUN` instead — it exists for that command only and is never written to a layer (see the single-stage example). Plain `ARG`/`ENV` stays fine for the non-secret project ID and host. ++- **Composite / reusable actions can't read `secrets`.** Inside a `.github/actions/*/action.yml` only `${{ inputs.* }}` is available. Add an `inputs:` entry per credential, reference `${{ inputs.* }}` there, and pass `${{ secrets.* }}` from the calling workflow's `with:` block. ++- **Build over SSH:** the runner's env doesn't reach the remote box. Set the vars inline immediately before the build command inside the `script:`. `${{ secrets.* }}` is substituted by Actions *before* the script is sent, so the value travels with the script. ++- **The worked examples are exemplars, not an allowlist.** For any provider not shown (GitLab CI, CircleCI, Jenkins, Bitbucket, Azure Pipelines, …), apply the same principle with your knowledge of that provider: find the job that runs the production build, expose the credentials there via the provider's native secret mechanism (GitLab project CI/CD variables, CircleCI project env vars / contexts, Jenkins credentials + `withCredentials`, …), and cross the same boundaries the same way — Docker builds still need `--build-arg`, SSH sessions still need inline vars. ++- **Make only the edits the provider actually needs.** Some providers inject project-level variables straight into every job's environment — GitLab CI/CD variables work this way — so an inline build step may need **no functional pipeline change at all**. When that's the conclusion, still add a short comment on the build job naming the required variables and where to create them (see the GitLab example) — the requirement must be visible in the repo, not only in your hand-off — and tell the user exactly which variables to create and where. ++- You can't create CI secrets. Whenever the pipeline reads a credential, tell the user where to add it before their next deploy — GitHub: **Settings → Secrets and variables → Actions**; GitLab: **Settings → CI/CD → Variables**; other providers: their equivalent secret store. The pipeline can't read a secret that doesn't exist yet. ++ ++#### Examples ++- **Dockerfile build stage (e.g. `Dockerfile`, no CI)** Declare the credentials as build args and promote them to env vars *before* the build `RUN`, in the build stage: ++ ```dockerfile ++ FROM node:22-slim AS build ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_API_KEY ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_API_KEY=$POSTHOG_CLI_API_KEY \ ++ POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN npm run build # now sees the upload credentials ++ ``` ++ With no CI wiring the image, tell the user to pass them when they build: `docker build --build-arg POSTHOG_CLI_API_KEY=… --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .` ++- **Single-stage Dockerfile (BuildKit secret)** When build and runtime share one stage, pass the API key as a BuildKit secret so it never lands in the image; keep `ARG`/`ENV` for the non-secret project ID and host: ++ ```dockerfile ++ # syntax=docker/dockerfile:1 ++ FROM node:22-slim ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY \ ++ npm run build ++ ``` ++ Build with `docker build --secret id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .`. In `docker/build-push-action`, pass the key through the `secrets:` input (`POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }}`) instead of `build-args:`. The `env=` attribute on `--mount` needs a current BuildKit — keep the `# syntax=docker/dockerfile:1` line; on engines too old for it, read the file form instead: `RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY POSTHOG_CLI_API_KEY=$(cat /run/secrets/POSTHOG_CLI_API_KEY) npm run build`. ++- **GitHub Actions — inline build step** Build runs on the runner; expose the creds with `env:` on that step: ++ ```yaml ++ - name: Build ++ run: npm run build ++ env: ++ POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — `docker build` / `docker/build-push-action`** Add the `ARG`/`ENV` to the Dockerfile build stage (above), then forward the creds as build args. Raw `docker build` takes `--build-arg`; `docker/build-push-action` takes a multi-line `build-args:` input — **merge into the existing `with:` block, don't add a second step**: ++ ```yaml ++ - name: Build and push image ++ uses: docker/build-push-action@v6 ++ with: ++ context: . ++ file: Dockerfile ++ push: true ++ tags: ${{ steps.meta.outputs.tags }} ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID=${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST=${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — nested/composite action** When the workflow delegates the build with `uses: ./.github/actions/build-and-push`, the `build-push-action` lives in that action's `action.yml`, which can't see `secrets`. Thread them through as inputs. In `.github/actions/build-and-push/action.yml`: ++ ```yaml ++ inputs: ++ posthog-cli-api-key: ++ required: true ++ posthog-cli-project-id: ++ required: true ++ posthog-cli-host: ++ required: true ++ runs: ++ using: composite ++ steps: ++ - uses: docker/build-push-action@v6 ++ with: ++ # ...existing context/file/push/tags... ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ inputs.posthog-cli-api-key }} ++ POSTHOG_CLI_PROJECT_ID=${{ inputs.posthog-cli-project-id }} ++ POSTHOG_CLI_HOST=${{ inputs.posthog-cli-host }} ++ ``` ++ Then pass the secrets from the calling workflow's `with:` block: ++ ```yaml ++ - uses: ./.github/actions/build-and-push ++ with: ++ # ...existing inputs... ++ posthog-cli-api-key: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ posthog-cli-project-id: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ posthog-cli-host: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — build over SSH** When a step SSHes into a server and runs the build there (e.g. `appleboy/ssh-action` with `git pull && npm run build`), set the vars inline right before the build command inside the `script:` — mirror however the script already passes runtime vars: ++ ```yaml ++ - uses: appleboy/ssh-action@v1 ++ with: ++ host: ${{ secrets.DEPLOY_HOST }} ++ # ... ++ script: | ++ cd /srv/app && git pull --ff-only origin main && npm ci ++ POSTHOG_CLI_API_KEY="${{ secrets.POSTHOG_CLI_API_KEY }}" \ ++ POSTHOG_CLI_PROJECT_ID="${{ secrets.POSTHOG_CLI_PROJECT_ID }}" \ ++ POSTHOG_CLI_HOST="${{ secrets.POSTHOG_CLI_HOST }}" \ ++ npm run build ++ ``` ++- **GitLab CI (`.gitlab-ci.yml`)** Project CI/CD variables are injected into every job's environment automatically, so a job that runs the build inline (`script: - npm run build`) needs **no functional YAML change** — no `variables:` block, and do NOT add a script line that writes the variables into a `.env` file (`printf … > .env`, `echo … >> .env`, etc.); the build already sees them as environment variables, which take precedence over any dotenv file. DO leave a comment on the build job so the requirement is visible in the repo, not only in your hand-off: ++ ```yaml ++ build: ++ stage: build ++ # PostHog source map upload: this job needs POSTHOG_CLI_API_KEY, ++ # POSTHOG_CLI_PROJECT_ID and POSTHOG_CLI_HOST available as CI/CD ++ # variables (Settings → CI/CD → Variables); GitLab injects them into ++ # the job automatically. Mark them Masked — but Protected only if this ++ # job runs exclusively on protected branches, otherwise feature-branch ++ # builds fail with missing credentials. ++ script: ++ - npm ci ++ - npm run build ++ ``` ++ Then tell the user to add those variables in **Settings → CI/CD → Variables** and the next pipeline picks them up. Edits beyond the comment are only needed when a boundary is crossed: a job that runs `docker build` must forward them (`--build-arg POSTHOG_CLI_API_KEY="$POSTHOG_CLI_API_KEY" …`) into the Dockerfile's build stage (see the Dockerfile example), and a job that builds over SSH must set them inline before the remote build command, exactly like the SSH example above. ++- **Other CI providers (CircleCI, Jenkins, Bitbucket, Azure Pipelines, …)** Same recipe, provider-native mechanics: open the pipeline config, find the job that runs the production build, expose the credentials to that job via the provider's secret store, and thread them through any Docker/SSH boundary just like the examples above. Reference credentials by name only, then tell the user each secret to create and exactly where in the provider's UI it goes. ++- **Untraceable setup** No `Dockerfile`, no CI config, and no build step you can trace: make no CI changes — do **not** author a new workflow, pipeline, or deploy file to fill the gap. Tell the user that wherever their production build command runs, it must have the upload credentials (`POSTHOG_CLI_*` / `POSTHOG_*`) available as environment variables, or maps won't upload on deploy. If part of the path is still recognisable — e.g. a `Dockerfile` built by an unfamiliar CI — wire the layers you do recognise and tell the user exactly what the remaining layer must pass in (e.g. the `--build-arg` flags). ++ ++### Associate the release with a git commit ++ ++`posthog-cli` links the release to a **git commit, branch and repo** so Error Tracking can show which deploy an error came from. It auto-detects that from the CI's git env vars or a local `.git` directory — you never touch the CLI invocation itself (it's usually baked into `npm run build` or a bundler plugin), you just make the git context available in the build environment. A `docker build` is where this breaks: it sees **neither** the env vars nor `.git` (the same boundary credentials hit), so the release ends up linked to nothing unless you forward the vars in. ++ ++#### Tips ++- **Forward GitHub's git env vars into the Docker build** the same way you forwarded credentials. Declare each as an `ARG` **and** promote it to `ENV` — `ARG` alone isn't visible to the CLI's env lookup. That's all auto-detection needs; no CLI flags, no `.git`. ++ ++#### Examples ++- **GitHub Actions → docker build** Forward GitHub's git vars into the build stage and the CLI auto-detects branch + repo + commit: ++ ```yaml ++ build-args: | ++ GITHUB_ACTIONS=true ++ GITHUB_SHA=${{ github.sha }} ++ GITHUB_REF_NAME=${{ github.ref_name }} ++ GITHUB_REPOSITORY=${{ github.repository }} ++ GITHUB_SERVER_URL=${{ github.server_url }} ++ ``` ++ Then in the build stage, declare each as `ARG` and re-export it as `ENV` before the build runs. ++- **Inline CI build (no Docker)** GitHub Actions already sets these vars on the runner, so auto-detection just works — nothing to pass. ++ ++### Test the local setup ++ ++Optionally add a temporary, clearly-labeled affordance that captures one test exception, so you can confirm errors arrive in Error Tracking with a source-resolved stack trace after the next production build. Always remove it afterwards. ++ ++#### Tips ++- The handler must call the SDK's exception-capture method **directly** — do **not** `throw`. Throwing depends on the global error handler and shows a dev overlay; a direct capture is deterministic across platforms. ++- Pass a single Error (or platform-equivalent throwable). No custom message beyond the Error, no extra properties, no second argument — the Error's stack trace is what gets resolved. ++- Use distinctive copy on the trigger (button label / route path) so the resulting event is easy to find in the UI. ++- Read any file before editing it and capture its exact contents; after testing, restore every file the affordance touched — the affordance only, leave the upload and credential wiring in place — and re-read to confirm nothing is left behind. Never leave the affordance in place — even if the test "didn't work", revert first. ++- The upload only happens on the *production build*: build, run, trigger the error, then confirm the stack trace in Error Tracking points at real source files, not minified bundle paths. ++ ++#### Examples ++- **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: ++ ```kotlin ++ import com.posthog.PostHog ++ ++ PostHog.captureException(Throwable("PostHog source maps test")) ++ ``` ++ Test flow — the upload only runs on the **minified release variant**: `./gradlew installRelease` (or Android Studio ▸ Build Variants ▸ release, then Run), launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **iOS (Swift)** `Button` on the root view (SwiftUI) or `UIButton` on the root view controller (UIKit), handler: ++ ```swift ++ do { ++ throw NSError(domain: "PostHogSourceMapTest", code: 1, ++ userInfo: [NSLocalizedDescriptionKey: "Source map upload test error"]) ++ } catch { ++ PostHogSDK.shared.captureException(error) ++ } ++ ``` ++ (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. ++- **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. ++ ++### Verify and hand off ++ ++Confirm the upload landed and report what changed. ++ ++#### Tips ++- Source maps upload during the **production build** — the build must actually run for a symbol set to appear. ++- Verify in PostHog Error Tracking settings on the **Symbol sets** page: a new symbol set should appear after the build completes. ++- When handing off, list the files you edited (paths only), the env-var **key** names you set (never values), whether a test affordance was added and reverted, and the exact build command to run. ++- If you wired CI, list the pipeline files you changed (`Dockerfile`, workflow, pipeline config) and spell out every manual follow-up — e.g. the secrets the user must add in their CI provider's settings before their next deploy, or the note that their build path couldn't be traced. ++ ++## General tips ++- The reference files for Node.js are authoritative — if this page and a reference disagree on an API, follow the reference. ++- Two different keys, two different jobs: a **personal API key** uploads maps at build time; the **public project key** powers the SDK at runtime. Don't swap them. ++- Keep build artifacts and uploaded maps in sync — every deploy should inject + upload within the same build so stack traces always resolve. ++- Uploaded maps live in PostHog and never need to be served publicly. ++- Detect the project's package manager before installing any dependency. ++- Read a file (and note its exact contents) immediately before editing it — essential for any temporary test code you'll revert afterwards. ++ ++## Framework guidelines ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- posthog-node is the Node.js server-side SDK package name; posthog-js is browser-only, so use posthog-node on the server instead ++- Include enableExceptionAutocapture: true in the PostHog constructor options ++- Add posthog.capture() calls in route handlers for meaningful user actions – every route that creates, updates, or deletes data should track an event with contextual properties ++- Add posthog.captureException(err, distinctId) in the application's error handler (e.g., Express error middleware, Fastify setErrorHandler, Koa app.on('error')) ++- The SDK batches events and flushes asynchronously. await flush() or await shutdown() before letting that process exit. If unsure, set flushAt 1 and flushInterval 0. ++- `posthog.capture()` enqueues synchronously and returns; the batched HTTP send happens afterwards. Treat every per-request handler as short-lived even when the framework feels like a server: Next.js / Nuxt / SvelteKit / Remix route handlers, serverless and edge functions, and Lambda are torn down per invocation before the send runs. Create the client with flushAt 1 and flushInterval 0, then await the send before returning. Always use `await posthog.flush()` for a shared/singleton client, `await posthog.shutdown()` for a per-request client. Never skip the awaited flush or risk the enqueued event being silently dropped. ++- Reverse proxy is NOT needed for server-side Node.js – only client-side JavaScript needs a proxy to avoid ad blockers ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md b/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md +new file mode 100644 +index 0000000..11206d5 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/COMMANDMENTS.md +@@ -0,0 +1,15 @@ ++# Framework rules ++ ++Follow these when integrating PostHog into this framework. ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- posthog-node is the Node.js server-side SDK package name; posthog-js is browser-only, so use posthog-node on the server instead ++- Include enableExceptionAutocapture: true in the PostHog constructor options ++- Add posthog.capture() calls in route handlers for meaningful user actions – every route that creates, updates, or deletes data should track an event with contextual properties ++- Add posthog.captureException(err, distinctId) in the application's error handler (e.g., Express error middleware, Fastify setErrorHandler, Koa app.on('error')) ++- The SDK batches events and flushes asynchronously. await flush() or await shutdown() before letting that process exit. If unsure, set flushAt 1 and flushInterval 0. ++- `posthog.capture()` enqueues synchronously and returns; the batched HTTP send happens afterwards. Treat every per-request handler as short-lived even when the framework feels like a server: Next.js / Nuxt / SvelteKit / Remix route handlers, serverless and edge functions, and Lambda are torn down per invocation before the send runs. Create the client with flushAt 1 and flushInterval 0, then await the send before returning. Always use `await posthog.flush()` for a shared/singleton client, `await posthog.shutdown()` for a per-request client. Never skip the awaited flush or risk the enqueued event being silently dropped. ++- Reverse proxy is NOT needed for server-side Node.js – only client-side JavaScript needs a proxy to avoid ad blockers ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md +new file mode 100644 +index 0000000..51bfb01 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/cli.md +@@ -0,0 +1,150 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ ++# Upload source maps with CLI - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Download CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++ If you already keep your project's configuration in a dotenv-style file, you can load these variables from it with the `--dotenv-file` option instead of exporting them: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --dotenv-file .env sourcemap upload --directory ./path/to/assets ++ ``` ++ ++3. 3 ++ ++ ## Inject ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++ You can verify that the metadata has been injected by checking for the `//# chunkId=...` comment in the minified code. ++ ++4. 4 ++ ++ ## Upload ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++5. 5 ++ ++ ## Serve injected assets ++ ++ Required ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++7. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/node.md b/.claude/skills/error-tracking-upload-source-maps-node/references/node.md +new file mode 100644 +index 0000000..16626cc +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/node.md +@@ -0,0 +1,166 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Node.js - Docs ++ ++Copy page ++ ++# Upload source maps for Node.js - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Install the PostHog CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate the PostHog CLI ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++3. 3 ++ ++ ## Output source maps for Node.js ++ ++ Required ++ ++ *Your goal in this step: Configure your build to generate source maps.* ++ ++ If you serve minified bundles in production, PostHog requires source maps to generate accurate stack traces. Here are instructions to enable source map generation for popular build tools: ++ ++ | Build Tool | Documentation | ++ | --- | --- | ++ | Vite | [Source Map Configuration](https://v3.vitejs.dev/config/build-options.html#build-sourcemap) | ++ | webpack | [Source Map Configuration](https://webpack.js.org/configuration/devtool/) | ++ | Rollup | [Source Map Options](https://rollupjs.org/configuration-options/#output-sourcemap) | ++ ++ For other build tools, consult their documentation to enable source maps. ++ ++4. 4 ++ ++ ## Inject source map ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++5. ## Verify source map injection ++ ++ Checkpoint ++ ++ Confirm that the served files are injected with the correct source map comment in production in dev tools: ++ ++ JavaScript ++ ++ PostHog AI ++ ++ ```javascript ++ //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c ++ ``` ++ ++6. 5 ++ ++ ## Upload source map ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++ #### Serve injected assets ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. We suggest you upload source maps right after your production build in CI. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++8. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md +new file mode 100644 +index 0000000..b6ae318 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-node/references/upload-source-maps.md +@@ -0,0 +1,67 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ ++# Upload source maps - Docs ++ ++If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. ++ ++If your source maps are not publicly hosted, you will need to upload them during your build process to see unminified code in your stack traces. ++ ++## AI wizard ++ ++If you're using a JavaScript or TypeScript framework, set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++Otherwise, choose your platform below for manual instructions. ++ ++## Platforms ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)Web](/docs/error-tracking/upload-source-maps/web.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nextjs.svg)Next.js](/docs/error-tracking/upload-source-maps/nextjs.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/nodejs.svg)Node.js](/docs/error-tracking/upload-source-maps/node.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React](/docs/error-tracking/upload-source-maps/react.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/docs/integrate/frameworks/angular.svg)Angular](/docs/error-tracking/upload-source-maps/angular.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nuxt.svg)Nuxt](/docs/error-tracking/upload-source-maps/nuxt.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React Native](/docs/error-tracking/upload-source-maps/react-native.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Android_robot_bec2fb7318.svg)Android](/docs/error-tracking/upload-mappings/android.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/webpack_3fc774b5a5.svg)Webpack](/docs/error-tracking/upload-source-maps/webpack.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Vitejs_logo_98ffe5d5ee.svg)Vite](/docs/error-tracking/upload-source-maps/vite.md) ++ ++- [CLI](/docs/error-tracking/upload-source-maps/cli.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/package-lock.json b/package-lock.json +index 0718219..d6906bf 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -11,10 +11,56 @@ + "posthog-node": "^5.35.4" + }, + "devDependencies": { ++ "@posthog/cli": "^0.16.0", + "@types/node": "^26.0.0", + "typescript": "^5.7.2" + } + }, ++ "node_modules/@posthog/cli": { ++ "version": "0.16.0", ++ "resolved": "https://registry.npmjs.org/@posthog/cli/-/cli-0.16.0.tgz", ++ "integrity": "sha512-W3di3Oy8jRZ0gUHEP6CG8OFLcKFvR4NNeZ3wqNeiY13pDHFZyXTPxsGTCwYUr5CmrToNvisEnR7KPYIGUzxbLg==", ++ "dev": true, ++ "hasInstallScript": true, ++ "hasShrinkwrap": true, ++ "license": "MIT", ++ "dependencies": { ++ "detect-libc": "^2.1.2" ++ }, ++ "bin": { ++ "posthog-cli": "run-posthog-cli.js" ++ }, ++ "engines": { ++ "node": ">=14.14", ++ "npm": ">=6" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/detect-libc": { ++ "version": "2.1.2", ++ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", ++ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", ++ "dev": true, ++ "license": "Apache-2.0", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/prettier": { ++ "version": "3.8.3", ++ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", ++ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", ++ "extraneous": true, ++ "license": "MIT", ++ "bin": { ++ "prettier": "bin/prettier.cjs" ++ }, ++ "engines": { ++ "node": ">=14" ++ }, ++ "funding": { ++ "url": "https://github.com/prettier/prettier?sponsor=1" ++ } ++ }, + "node_modules/@posthog/core": { + "version": "1.29.13", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.29.13.tgz", +diff --git a/package.json b/package.json +index d9035ab..0541387 100644 +--- a/package.json ++++ b/package.json +@@ -4,13 +4,14 @@ + "private": true, + "type": "module", + "scripts": { +- "build": "tsc", ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name node-raw", + "start": "node --env-file=.env dist/index.js" + }, + "dependencies": { + "posthog-node": "^5.35.4" + }, + "devDependencies": { ++ "@posthog/cli": "^0.16.0", + "@types/node": "^26.0.0", + "typescript": "^5.7.2" + } +diff --git a/posthog-source-maps-report.md b/posthog-source-maps-report.md +new file mode 100644 +index 0000000..83d7125 +--- /dev/null ++++ b/posthog-source-maps-report.md +@@ -0,0 +1,58 @@ ++# PostHog source map upload setup ++ ++## Changes made ++ ++- `package.json` ++- `package-lock.json` ++- `tsconfig.json` ++- `.env` ++ ++The TypeScript build now emits external source maps with embedded source content. The production build then runs PostHog CLI processing against `dist`, injecting chunk IDs and uploading the matching source maps. ++ ++## Commands ++ ++Production build and upload: ++ ++```sh ++npm run build ++``` ++ ++Expanded build/upload command: ++ ++```sh ++tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name node-raw ++``` ++ ++Run the built application: ++ ++```sh ++npm run start ++``` ++ ++## Environment keys ++ ++The following keys are configured in the gitignored `.env` file: ++ ++- `POSTHOG_CLI_API_KEY` ++- `POSTHOG_CLI_PROJECT_ID` ++- `POSTHOG_CLI_HOST` ++ ++No secret values are included in this report. ++ ++## CI/CD follow-up ++ ++No Dockerfile or CI/CD configuration exists in this project, so no deployment pipeline could be traced or edited. Wherever `npm run build` runs in production, provide these environment variables through that platform's secret store: ++ ++- `POSTHOG_CLI_API_KEY` ++- `POSTHOG_CLI_PROJECT_ID` ++- `POSTHOG_CLI_HOST` ++ ++Do not create a `.env` file in CI or commit the personal API key. The build command keeps `--dotenv-file .env`; real CI environment variables take precedence, and a missing dotenv file is skipped with a warning. ++ ++## Verification ++ ++A temporary direct `captureException` test was added, exercised through the production build/run flow, and reverted. The permanent source-map configuration remains. ++ ++1. Run `npm run build` for every production release. ++2. Confirm a new upload appears on the [PostHog Symbol sets page](https://us.posthog.com/project/228144/error_tracking/configuration). ++3. Trigger a captured production exception and confirm its stack trace resolves to original TypeScript source rather than generated JavaScript paths. +diff --git a/tsconfig.json b/tsconfig.json +index 2659d22..0750fd3 100644 +--- a/tsconfig.json ++++ b/tsconfig.json +@@ -5,6 +5,8 @@ + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", ++ "sourceMap": true, ++ "inlineSources": true, + "strict": true, + "skipLibCheck": true + }, diff --git a/results/source-maps-sol-medium/node-raw__sol-medium/result.json b/results/source-maps-sol-medium/node-raw__sol-medium/result.json new file mode 100644 index 000000000..6d9e659cb --- /dev/null +++ b/results/source-maps-sol-medium/node-raw__sol-medium/result.json @@ -0,0 +1,24 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "posthog-node", + "@posthog/cli" + ], + "envFile": "/tmp/sm-run-node-raw__sol-medium/.env", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file diff --git a/results/source-maps-sol-medium/react-vite__anthropic/app.diff b/results/source-maps-sol-medium/react-vite__anthropic/app.diff new file mode 100644 index 000000000..fea9aa917 --- /dev/null +++ b/results/source-maps-sol-medium/react-vite__anthropic/app.diff @@ -0,0 +1,1665 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/.posthog-wizard b/.claude/skills/error-tracking-upload-source-maps-vite/.posthog-wizard +new file mode 100644 +index 0000000..e69de29 +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md +new file mode 100644 +index 0000000..000cc83 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md +@@ -0,0 +1,408 @@ ++--- ++name: error-tracking-upload-source-maps-vite ++description: Upload source maps to PostHog Error Tracking for Vite ++metadata: ++ author: PostHog ++ version: 1.49.1 ++--- ++ ++# Upload source maps to PostHog for Vite ++ ++This skill helps you upload source maps (or platform debug symbols) so PostHog Error Tracking can resolve minified stack traces back to your original source. ++ ++## Reference files ++ ++- `references/vite.md` - Upload source maps for vite - docs ++- `references/upload-source-maps.md` - Upload source maps - docs ++- `references/cli.md` - Upload source maps with cli - docs ++- `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow ++ ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. ++ ++## Steps ++ ++The stages of wiring up source map upload, in order. Each step has a short overview, gotchas under **Tips**, and per-technology notes under **Examples**. The reference files above are the source of truth for the exact, per-framework API — when this page and a reference disagree, follow the reference for Vite. ++ ++### Get a personal API key ++ ++Source map upload authenticates with a **personal API key**, not the public project API key the SDK uses at runtime. The key needs error-tracking write access; the quickest path is the "Source map upload" preset on PostHog's personal API keys settings page. ++ ++#### Tips ++- The public project key (the one in your SDK `init`) will **not** work for uploads — it has no write scope for symbol sets. ++- Never hardcode the key in source. It belongs in an environment variable read at build time (see "Write credentials to the env file"). ++- Keys can't be minted programmatically — create them by hand in PostHog settings, then store the value as a secret. ++ ++### Apply build-config changes ++ ++Wire source map generation, chunk-ID injection, and upload into your **production build** so every deploy ships matching maps. Depending on the platform this is either a build/bundler plugin, or a `posthog-cli sourcemap process` step run after the build (it injects chunk IDs and uploads in one pass). Follow the Vite reference for the exact wiring. ++ ++#### Tips ++- If you wire `posthog-cli` directly (no framework or bundler plugin), generating the maps is **your** responsibility — the CLI only injects chunk IDs into, and uploads, maps your build already produced. Two things must be true before `posthog-cli sourcemap process` works: ++ - Source maps are emitted next to your output bundles (e.g. `.js.map` files). ++ - The maps include `sourcesContent` (the original source embedded inside the map). Without it PostHog has the line/column mappings but not the code, so traces can't be fully resolved. ++- **Inject before deploy**: the *injected* bundles must be the ones shipped to production. Bundles missing the `//# chunkId=…` comment can't be matched to uploaded maps. ++- Wire injection + upload into the build itself (plugin, post-build script, or CI step) — manual uploads drift from deployed code. ++- **Don't ship source maps publicly**: omit `.map` files from the deployed artifact, or use hidden source maps. Uploaded maps live in PostHog, not on your origin. ++- **Link each release to its commit.** The CLI auto-detects the commit from the CI's git env vars — see "Associate the release with a git commit" for making those reachable in Docker/CI builds. ++ ++#### Examples ++- **Node / tsc** Emit maps with embedded sources by setting both in `tsconfig.json`: `"sourceMap": true` and `"inlineSources": true`. Then run `posthog-cli sourcemap process` against the build output dir as a post-build step — it injects chunk IDs and uploads in one pass, and needs the upload credentials (see "Make credentials available at build time"). ++- **Vite / Webpack / Rollup** Prefer the bundler plugin from the reference over hand-rolling the CLI — it injects and uploads in one pass. Make sure the bundler is configured to emit source maps. ++- **iOS (Xcode)** iOS uploads **dSYM debug symbols**, not source maps. Required target changes: ++ 1. `DEBUG_INFORMATION_FORMAT = dwarf-with-dsym` for Release. ++ 2. `ENABLE_USER_SCRIPT_SANDBOXING = NO`. ++ 3. A Run Script phase, ordered last, with `$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)` in its Input Files, calling the SDK's bundled script — do not hand-roll the upload: ++ - SPM: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/posthog-ios/build-tools/upload-symbols.sh"` ++ - CocoaPods: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh"` ++ Copy the invocation verbatim — the `POSTHOG_INCLUDE_SOURCE=1` and `POSTHOG_CLI_DOTENV_FILE` prefixes HAVE to be there. This needs a recent `posthog-cli` (older ones silently ignore `POSTHOG_CLI_DOTENV_FILE`); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++- **Android (Gradle)** Android uploads **ProGuard/R8 mapping files**, not source maps. Apply the `com.posthog.android` Gradle plugin on the **app module's** `build.gradle(.kts)` (never the root project), per the reference — the plugin hooks the build and uploads automatically, do not hand-roll a `posthog-cli` step. Gotchas: ++ 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. ++ 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++ 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. ++- **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. ++- **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. ++ - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. ++ - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. ++ - **iOS** Follow the **iOS (Xcode)** bullet above, on the **Runner** target in `ios/Runner.xcworkspace`. Flutter is always CocoaPods: `${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh`. ++ ++ Set `captureNativeExceptions = true` in `PostHogConfig.errorTrackingConfig` — it defaults to `false`, and while it's off the native SDKs capture nothing to symbolicate. ++ ++### Make credentials available at build time ++ ++The upload credentials must be readable **by the build pipeline at build time**, not merely present in a `.env` file. Whether `.env` is auto-loaded depends on the technology. ++ ++#### Tips ++- **Auto-loads `.env`**: Next.js, Nuxt and similar frameworks read `.env` into the build for you — nothing extra to do. ++- **Vite is a partial exception**: it auto-loads `.env` into `import.meta.env` for client code (only `VITE_`-prefixed vars), but does **not** put vars in `process.env` for your config to read. The upload credentials (`POSTHOG_*`, not `VITE_`-prefixed) are read when the plugin is constructed, so load them yourself — see the Vite example below. ++- **Does NOT auto-load `.env`**: Rollup, plain webpack, and plain Node scripts. Load it explicitly — add `dotenv` (`require('dotenv').config()`, or `import 'dotenv/config'` for ESM) at the top of the bundler/config file. ++- **Separate-process gotcha**: if `posthog-cli sourcemap process` runs as its own `package.json` step (after the bundler), the CLI call is a **separate child process** and will *not* see env vars a loader set inside the bundler config. Point the CLI at the file directly: `posthog-cli --dotenv-file sourcemap process …` (the flag goes before the subcommand). ++- **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) ++- **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. ++- **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. ++- **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: ++ - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). ++ - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. ++ - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. ++ ++#### Examples ++- **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. ++- **Vite** Export `vite.config` as a function and merge `loadEnv` into `process.env` so the config (and the PostHog plugin) can read the upload credentials. Pass `''` as the third arg so non-`VITE_` vars like `POSTHOG_API_KEY` are included — the default `'VITE_'` prefix skips them: ++ ```ts ++ import { defineConfig, loadEnv } from 'vite'; ++ ++ export default ({ mode }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') }; ++ // process.env.POSTHOG_API_KEY is now readable by the plugins below ++ return defineConfig({ ++ plugins: [/* … posthog source map plugin … */], ++ }); ++ }; ++ ``` ++- **Rollup / webpack / plain Node** Add `import 'dotenv/config'` (or `require('dotenv').config()`) at the top of the config/entry file so the loader runs before the build reads the vars. ++- **Standalone posthog-cli step** Pass `--dotenv-file .env` to the `process` invocation so it can authenticate: ++ ```json ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name my-app" ++ ``` ++- **iOS (Xcode / posthog-cli)** A gitignored `.env` next to the `.xcodeproj` — the Run Script invocation's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix hands it to posthog-cli. No Xcode project wiring beyond the Run Script phase. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Android (Gradle / posthog-cli)** A gitignored `.env` at the Gradle project root, bridged into the upload tasks in the **app module's** `build.gradle.kts`: ++ ```kotlin ++ import com.posthog.android.PostHogCliExecTask ++ import java.util.Properties ++ ++ val postHogEnv = Properties().apply { ++ val envFile = rootProject.file(".env") ++ if (envFile.exists()) envFile.inputStream().use { load(it) } ++ } ++ ++ tasks.withType().configureEach { ++ postHogEnv.getProperty("POSTHOG_CLI_API_KEY")?.let { postHogApiKey.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_PROJECT_ID")?.let { postHogProjectId.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_HOST")?.let { postHogHost.set(it) } ++ } ++ ``` ++ (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. ++ ++### Write credentials to the env file ++ ++Write the personal API key and project identifiers into the env file your build reads. Reuse the file the project already uses — don't introduce a second one. ++ ++#### Tips ++- Picking the file: if an env file already contains PostHog vars (`POSTHOG_*` / `NEXT_PUBLIC_POSTHOG_*`), use that one. Otherwise, if exactly one env file exists use it; if several exist prefer `.env`. Only create a new file when none exists. ++- Variable names depend on which uploader you wired: ++ - `posthog-cli` direct upload → `POSTHOG_CLI_API_KEY`, `POSTHOG_CLI_PROJECT_ID`, `POSTHOG_CLI_HOST` ++ - bundler-plugin variants → `POSTHOG_API_KEY`, `POSTHOG_PROJECT_ID`, `POSTHOG_HOST` ++- Set the `*_HOST` var when you're not on US Cloud's default (e.g. EU Cloud or self-hosted); setting it explicitly always is safe. Follow the reference for the variant. ++- In CI/CD, set the same vars as secrets — never commit the key. ++ ++### Identify the build and run commands ++ ++Resolve two concrete commands for this project: the production **build** command (the one that uploads source maps) and the **run** command that launches the built app (so a test error can be triggered against the real artifact). ++ ++#### Tips ++- Resolve real commands from the project's actual scripts/config — substitute the correct package manager. Never leave a generic "start the app". ++- When a build artifact is involved, prefer the command that serves the *production* build over the dev server. ++ ++#### Examples ++- **Next.js** Build: `npm run build` (`next build`). Run: `npm run start` (`next start`). ++- **Vite** Build: `npm run build`. Run: `npm run preview`. ++- **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). ++- **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). ++- **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). ++- **Flutter** One pair per platform you wired: ++ - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. ++ - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. ++ - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. ++ ++### Set up CI for automatic uploads ++ ++Source maps are only uploaded when the **production build** runs, so the environment that builds and deploys your app needs the same upload credentials you put in the env file. The whole job is: **find where the production build command actually runs, then make the upload credentials reachable at that exact spot.** **Only ever edit CI/deploy files that already exist — never create a new workflow, pipeline, or deploy file.** Wiring credentials means modifying the build/deploy config this project already has; it is never license to author new CI. The build is where maps inject + upload, and env does **not** automatically cross three boundaries — into a Docker build, into a nested/composite action, or into an SSH session. So trace the deploy path before editing anything: ++ ++1. Is there a `Dockerfile`? If the build command runs inside it (`RUN `), the build happens in that image's **build stage**. ++2. Is there a workflow under `.github/workflows/`? Open it and find the step that triggers the build, then follow it to where the build truly executes — it may be: ++ - an inline build step (`run: npm run build`) on the runner, ++ - a `docker build` / `docker/build-push-action` step (build runs in the image), ++ - a `uses: ./.github/actions/...` **local composite action** — open that `action.yml`; the real build step is one layer down, ++ - an `ssh`/deploy step (e.g. `appleboy/ssh-action`) whose `script:` runs the build **on a remote server**. ++3. Any other CI config in the repo (`.gitlab-ci.yml`, `.circleci/config.yml`, `Jenkinsfile`, `bitbucket-pipelines.yml`, `azure-pipelines.yml`, …)? Open it and find the job/stage that runs the production build. The principle is identical; apply it with your working knowledge of that provider — the examples below show the pattern to mirror. ++4. No `Dockerfile`, no CI config, no build step you can trace? Don't guess — tell the user where the creds need to be (see "Untraceable setup" under Examples). ++ ++#### Tips ++- **A deploy file for another package is not license to author one for this one.** In a monorepo especially, finding a workflow that deploys a *sibling* package (e.g. a `deploy-backend.yml`, or a `Dockerfile`/pipeline for another app) does **not** mean you should create a matching `deploy-frontend.yml` (or any new CI file) for the project you're instrumenting. Wire credentials only into the existing file that builds *this* project. If this project has no build/deploy config you can open and edit, it is untraceable: make no CI changes and hand the requirement to the user (see "Untraceable setup") — do **not** invent one. ++- Reuse the **exact variable names** from "Write credentials to the env file" — the build reads the same names locally and in CI. (`POSTHOG_CLI_*` for direct `posthog-cli`; `POSTHOG_*` for bundler-plugin uploaders.) ++- **In CI, credentials travel as environment variables — never as a file.** Do not materialize a `.env` on the runner (e.g. `printf … > .env` before the build), and never copy or un-ignore one into a Docker image: it's redundant, and a secrets file on disk can leak into artifacts, caches, or image layers. A build script that passes `--dotenv-file .env` to `posthog-cli` works unchanged in CI even though `.env` doesn't exist there: real environment variables take precedence over the file, and a missing file is skipped with a warning. ++- **Never commit secret values.** Reference credentials by name only: Docker `ARG`/`ENV` or BuildKit secret ids, `${{ secrets.* }}` in GitHub Actions. The personal API key stays out of version control. ++- Layers stack — a workflow can call a composite action that runs `docker build` against a Dockerfile. Wire **every** layer the credentials must pass through, from the outer `${{ secrets.* }}` reference down to the `ARG`/`ENV` in the build stage. ++- **Multi-stage Dockerfiles:** put the `ARG`/`ENV` in the **build stage** (where the build command runs), never the runtime stage. That's both correct (the build needs them) and safer (the creds don't get baked into the shipped image). ++- **Single-stage Dockerfiles:** with no separate build stage, `ARG`/`ENV` would bake the API key into the shipped image (`docker inspect` reveals `ENV`; `docker history` can reveal build args). Mount the key as a **BuildKit secret** on the build `RUN` instead — it exists for that command only and is never written to a layer (see the single-stage example). Plain `ARG`/`ENV` stays fine for the non-secret project ID and host. ++- **Composite / reusable actions can't read `secrets`.** Inside a `.github/actions/*/action.yml` only `${{ inputs.* }}` is available. Add an `inputs:` entry per credential, reference `${{ inputs.* }}` there, and pass `${{ secrets.* }}` from the calling workflow's `with:` block. ++- **Build over SSH:** the runner's env doesn't reach the remote box. Set the vars inline immediately before the build command inside the `script:`. `${{ secrets.* }}` is substituted by Actions *before* the script is sent, so the value travels with the script. ++- **The worked examples are exemplars, not an allowlist.** For any provider not shown (GitLab CI, CircleCI, Jenkins, Bitbucket, Azure Pipelines, …), apply the same principle with your knowledge of that provider: find the job that runs the production build, expose the credentials there via the provider's native secret mechanism (GitLab project CI/CD variables, CircleCI project env vars / contexts, Jenkins credentials + `withCredentials`, …), and cross the same boundaries the same way — Docker builds still need `--build-arg`, SSH sessions still need inline vars. ++- **Make only the edits the provider actually needs.** Some providers inject project-level variables straight into every job's environment — GitLab CI/CD variables work this way — so an inline build step may need **no functional pipeline change at all**. When that's the conclusion, still add a short comment on the build job naming the required variables and where to create them (see the GitLab example) — the requirement must be visible in the repo, not only in your hand-off — and tell the user exactly which variables to create and where. ++- You can't create CI secrets. Whenever the pipeline reads a credential, tell the user where to add it before their next deploy — GitHub: **Settings → Secrets and variables → Actions**; GitLab: **Settings → CI/CD → Variables**; other providers: their equivalent secret store. The pipeline can't read a secret that doesn't exist yet. ++ ++#### Examples ++- **Dockerfile build stage (e.g. `Dockerfile`, no CI)** Declare the credentials as build args and promote them to env vars *before* the build `RUN`, in the build stage: ++ ```dockerfile ++ FROM node:22-slim AS build ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_API_KEY ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_API_KEY=$POSTHOG_CLI_API_KEY \ ++ POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN npm run build # now sees the upload credentials ++ ``` ++ With no CI wiring the image, tell the user to pass them when they build: `docker build --build-arg POSTHOG_CLI_API_KEY=… --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .` ++- **Single-stage Dockerfile (BuildKit secret)** When build and runtime share one stage, pass the API key as a BuildKit secret so it never lands in the image; keep `ARG`/`ENV` for the non-secret project ID and host: ++ ```dockerfile ++ # syntax=docker/dockerfile:1 ++ FROM node:22-slim ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY \ ++ npm run build ++ ``` ++ Build with `docker build --secret id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .`. In `docker/build-push-action`, pass the key through the `secrets:` input (`POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }}`) instead of `build-args:`. The `env=` attribute on `--mount` needs a current BuildKit — keep the `# syntax=docker/dockerfile:1` line; on engines too old for it, read the file form instead: `RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY POSTHOG_CLI_API_KEY=$(cat /run/secrets/POSTHOG_CLI_API_KEY) npm run build`. ++- **GitHub Actions — inline build step** Build runs on the runner; expose the creds with `env:` on that step: ++ ```yaml ++ - name: Build ++ run: npm run build ++ env: ++ POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — `docker build` / `docker/build-push-action`** Add the `ARG`/`ENV` to the Dockerfile build stage (above), then forward the creds as build args. Raw `docker build` takes `--build-arg`; `docker/build-push-action` takes a multi-line `build-args:` input — **merge into the existing `with:` block, don't add a second step**: ++ ```yaml ++ - name: Build and push image ++ uses: docker/build-push-action@v6 ++ with: ++ context: . ++ file: Dockerfile ++ push: true ++ tags: ${{ steps.meta.outputs.tags }} ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID=${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST=${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — nested/composite action** When the workflow delegates the build with `uses: ./.github/actions/build-and-push`, the `build-push-action` lives in that action's `action.yml`, which can't see `secrets`. Thread them through as inputs. In `.github/actions/build-and-push/action.yml`: ++ ```yaml ++ inputs: ++ posthog-cli-api-key: ++ required: true ++ posthog-cli-project-id: ++ required: true ++ posthog-cli-host: ++ required: true ++ runs: ++ using: composite ++ steps: ++ - uses: docker/build-push-action@v6 ++ with: ++ # ...existing context/file/push/tags... ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ inputs.posthog-cli-api-key }} ++ POSTHOG_CLI_PROJECT_ID=${{ inputs.posthog-cli-project-id }} ++ POSTHOG_CLI_HOST=${{ inputs.posthog-cli-host }} ++ ``` ++ Then pass the secrets from the calling workflow's `with:` block: ++ ```yaml ++ - uses: ./.github/actions/build-and-push ++ with: ++ # ...existing inputs... ++ posthog-cli-api-key: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ posthog-cli-project-id: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ posthog-cli-host: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — build over SSH** When a step SSHes into a server and runs the build there (e.g. `appleboy/ssh-action` with `git pull && npm run build`), set the vars inline right before the build command inside the `script:` — mirror however the script already passes runtime vars: ++ ```yaml ++ - uses: appleboy/ssh-action@v1 ++ with: ++ host: ${{ secrets.DEPLOY_HOST }} ++ # ... ++ script: | ++ cd /srv/app && git pull --ff-only origin main && npm ci ++ POSTHOG_CLI_API_KEY="${{ secrets.POSTHOG_CLI_API_KEY }}" \ ++ POSTHOG_CLI_PROJECT_ID="${{ secrets.POSTHOG_CLI_PROJECT_ID }}" \ ++ POSTHOG_CLI_HOST="${{ secrets.POSTHOG_CLI_HOST }}" \ ++ npm run build ++ ``` ++- **GitLab CI (`.gitlab-ci.yml`)** Project CI/CD variables are injected into every job's environment automatically, so a job that runs the build inline (`script: - npm run build`) needs **no functional YAML change** — no `variables:` block, and do NOT add a script line that writes the variables into a `.env` file (`printf … > .env`, `echo … >> .env`, etc.); the build already sees them as environment variables, which take precedence over any dotenv file. DO leave a comment on the build job so the requirement is visible in the repo, not only in your hand-off: ++ ```yaml ++ build: ++ stage: build ++ # PostHog source map upload: this job needs POSTHOG_CLI_API_KEY, ++ # POSTHOG_CLI_PROJECT_ID and POSTHOG_CLI_HOST available as CI/CD ++ # variables (Settings → CI/CD → Variables); GitLab injects them into ++ # the job automatically. Mark them Masked — but Protected only if this ++ # job runs exclusively on protected branches, otherwise feature-branch ++ # builds fail with missing credentials. ++ script: ++ - npm ci ++ - npm run build ++ ``` ++ Then tell the user to add those variables in **Settings → CI/CD → Variables** and the next pipeline picks them up. Edits beyond the comment are only needed when a boundary is crossed: a job that runs `docker build` must forward them (`--build-arg POSTHOG_CLI_API_KEY="$POSTHOG_CLI_API_KEY" …`) into the Dockerfile's build stage (see the Dockerfile example), and a job that builds over SSH must set them inline before the remote build command, exactly like the SSH example above. ++- **Other CI providers (CircleCI, Jenkins, Bitbucket, Azure Pipelines, …)** Same recipe, provider-native mechanics: open the pipeline config, find the job that runs the production build, expose the credentials to that job via the provider's secret store, and thread them through any Docker/SSH boundary just like the examples above. Reference credentials by name only, then tell the user each secret to create and exactly where in the provider's UI it goes. ++- **Untraceable setup** No `Dockerfile`, no CI config, and no build step you can trace: make no CI changes — do **not** author a new workflow, pipeline, or deploy file to fill the gap. Tell the user that wherever their production build command runs, it must have the upload credentials (`POSTHOG_CLI_*` / `POSTHOG_*`) available as environment variables, or maps won't upload on deploy. If part of the path is still recognisable — e.g. a `Dockerfile` built by an unfamiliar CI — wire the layers you do recognise and tell the user exactly what the remaining layer must pass in (e.g. the `--build-arg` flags). ++ ++### Associate the release with a git commit ++ ++`posthog-cli` links the release to a **git commit, branch and repo** so Error Tracking can show which deploy an error came from. It auto-detects that from the CI's git env vars or a local `.git` directory — you never touch the CLI invocation itself (it's usually baked into `npm run build` or a bundler plugin), you just make the git context available in the build environment. A `docker build` is where this breaks: it sees **neither** the env vars nor `.git` (the same boundary credentials hit), so the release ends up linked to nothing unless you forward the vars in. ++ ++#### Tips ++- **Forward GitHub's git env vars into the Docker build** the same way you forwarded credentials. Declare each as an `ARG` **and** promote it to `ENV` — `ARG` alone isn't visible to the CLI's env lookup. That's all auto-detection needs; no CLI flags, no `.git`. ++ ++#### Examples ++- **GitHub Actions → docker build** Forward GitHub's git vars into the build stage and the CLI auto-detects branch + repo + commit: ++ ```yaml ++ build-args: | ++ GITHUB_ACTIONS=true ++ GITHUB_SHA=${{ github.sha }} ++ GITHUB_REF_NAME=${{ github.ref_name }} ++ GITHUB_REPOSITORY=${{ github.repository }} ++ GITHUB_SERVER_URL=${{ github.server_url }} ++ ``` ++ Then in the build stage, declare each as `ARG` and re-export it as `ENV` before the build runs. ++- **Inline CI build (no Docker)** GitHub Actions already sets these vars on the runner, so auto-detection just works — nothing to pass. ++ ++### Test the local setup ++ ++Optionally add a temporary, clearly-labeled affordance that captures one test exception, so you can confirm errors arrive in Error Tracking with a source-resolved stack trace after the next production build. Always remove it afterwards. ++ ++#### Tips ++- The handler must call the SDK's exception-capture method **directly** — do **not** `throw`. Throwing depends on the global error handler and shows a dev overlay; a direct capture is deterministic across platforms. ++- Pass a single Error (or platform-equivalent throwable). No custom message beyond the Error, no extra properties, no second argument — the Error's stack trace is what gets resolved. ++- Use distinctive copy on the trigger (button label / route path) so the resulting event is easy to find in the UI. ++- Read any file before editing it and capture its exact contents; after testing, restore every file the affordance touched — the affordance only, leave the upload and credential wiring in place — and re-read to confirm nothing is left behind. Never leave the affordance in place — even if the test "didn't work", revert first. ++- The upload only happens on the *production build*: build, run, trigger the error, then confirm the stack trace in Error Tracking points at real source files, not minified bundle paths. ++ ++#### Examples ++- **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: ++ ```kotlin ++ import com.posthog.PostHog ++ ++ PostHog.captureException(Throwable("PostHog source maps test")) ++ ``` ++ Test flow — the upload only runs on the **minified release variant**: `./gradlew installRelease` (or Android Studio ▸ Build Variants ▸ release, then Run), launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **iOS (Swift)** `Button` on the root view (SwiftUI) or `UIButton` on the root view controller (UIKit), handler: ++ ```swift ++ do { ++ throw NSError(domain: "PostHogSourceMapTest", code: 1, ++ userInfo: [NSLocalizedDescriptionKey: "Source map upload test error"]) ++ } catch { ++ PostHogSDK.shared.captureException(error) ++ } ++ ``` ++ (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. ++- **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. ++ ++### Verify and hand off ++ ++Confirm the upload landed and report what changed. ++ ++#### Tips ++- Source maps upload during the **production build** — the build must actually run for a symbol set to appear. ++- Verify in PostHog Error Tracking settings on the **Symbol sets** page: a new symbol set should appear after the build completes. ++- When handing off, list the files you edited (paths only), the env-var **key** names you set (never values), whether a test affordance was added and reverted, and the exact build command to run. ++- If you wired CI, list the pipeline files you changed (`Dockerfile`, workflow, pipeline config) and spell out every manual follow-up — e.g. the secrets the user must add in their CI provider's settings before their next deploy, or the note that their build path couldn't be traced. ++ ++## General tips ++- The reference files for Vite are authoritative — if this page and a reference disagree on an API, follow the reference. ++- Two different keys, two different jobs: a **personal API key** uploads maps at build time; the **public project key** powers the SDK at runtime. Don't swap them. ++- Keep build artifacts and uploaded maps in sync — every deploy should inject + upload within the same build so stack traces always resolve. ++- Uploaded maps live in PostHog and never need to be served publicly. ++- Detect the project's package manager before installing any dependency. ++- Read a file (and note its exact contents) immediately before editing it — essential for any temporary test code you'll revert afterwards. ++ ++## Framework guidelines ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md +new file mode 100644 +index 0000000..64d9113 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md +@@ -0,0 +1,8 @@ ++# Framework rules ++ ++Follow these when integrating PostHog into this framework. ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md +new file mode 100644 +index 0000000..51bfb01 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md +@@ -0,0 +1,150 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ ++# Upload source maps with CLI - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Download CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++ If you already keep your project's configuration in a dotenv-style file, you can load these variables from it with the `--dotenv-file` option instead of exporting them: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --dotenv-file .env sourcemap upload --directory ./path/to/assets ++ ``` ++ ++3. 3 ++ ++ ## Inject ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++ You can verify that the metadata has been injected by checking for the `//# chunkId=...` comment in the minified code. ++ ++4. 4 ++ ++ ## Upload ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++5. 5 ++ ++ ## Serve injected assets ++ ++ Required ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++7. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md +new file mode 100644 +index 0000000..b6ae318 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md +@@ -0,0 +1,67 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ ++# Upload source maps - Docs ++ ++If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. ++ ++If your source maps are not publicly hosted, you will need to upload them during your build process to see unminified code in your stack traces. ++ ++## AI wizard ++ ++If you're using a JavaScript or TypeScript framework, set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++Otherwise, choose your platform below for manual instructions. ++ ++## Platforms ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)Web](/docs/error-tracking/upload-source-maps/web.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nextjs.svg)Next.js](/docs/error-tracking/upload-source-maps/nextjs.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/nodejs.svg)Node.js](/docs/error-tracking/upload-source-maps/node.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React](/docs/error-tracking/upload-source-maps/react.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/docs/integrate/frameworks/angular.svg)Angular](/docs/error-tracking/upload-source-maps/angular.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nuxt.svg)Nuxt](/docs/error-tracking/upload-source-maps/nuxt.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React Native](/docs/error-tracking/upload-source-maps/react-native.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Android_robot_bec2fb7318.svg)Android](/docs/error-tracking/upload-mappings/android.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/webpack_3fc774b5a5.svg)Webpack](/docs/error-tracking/upload-source-maps/webpack.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Vitejs_logo_98ffe5d5ee.svg)Vite](/docs/error-tracking/upload-source-maps/vite.md) ++ ++- [CLI](/docs/error-tracking/upload-source-maps/cli.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md +new file mode 100644 +index 0000000..9465e3f +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md +@@ -0,0 +1,103 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Vite - Docs ++ ++Copy page ++ ++# Upload source maps for Vite - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Install the PostHog Rollup plugin ++ ++ Required ++ ++ Vite uses Rollup under the hood, so you can use the PostHog Rollup plugin to upload source maps: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```shell ++ npm install @posthog/rollup-plugin ++ ``` ++ ++2. 2 ++ ++ ## Add PostHog plugin to your Vite config ++ ++ Required ++ ++ Add the PostHog plugin to your `vite.config.js` file: ++ ++ vite.config.js ++ ++ PostHog AI ++ ++ ```javascript ++ import { defineConfig } from 'vite' ++ import posthog from '@posthog/rollup-plugin' ++ export default defineConfig({ ++ plugins: [ ++ posthog({ ++ personalApiKey: process.env.POSTHOG_API_KEY!, // Personal API Key ++ projectId: process.env.POSTHOG_PROJECT_ID, // Project ID ++ host: process.env.POSTHOG_HOST, // (optional) defaults to https://us.i.posthog.com ++ sourcemaps: { // (optional) ++ enabled: true, // (optional) Enable sourcemaps generation and upload, defaults to true ++ releaseName: 'my-application', // (optional) Release name ++ releaseVersion: '1.0.0', // (optional) Release version ++ deleteAfterUpload: true, // (optional) Delete sourcemaps after upload, defaults to true ++ }, ++ }), ++ ], ++ }) ++ ``` ++ ++ Set the following environment variables: ++ ++ | Environment variable | Description | ++ | --- | --- | ++ | POSTHOG_API_KEY | [Personal API key](https://app.posthog.com/settings/user-api-keys#variables) with at least write access on error tracking | ++ | POSTHOG_PROJECT_ID | Project ID you can find in your [project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_HOST | (optional) Your PostHog instance URL. Defaults to https://us.i.posthog.com | ++ ++ **Using CI/CD?** ++ ++ Add these environment variables to your CI/CD service's project settings to automatically upload source maps during production builds. ++ ++3. ## Verify source map upload and injection ++ ++ Checkpoint ++ ++ Confirm source maps were successfully uploaded: ++ ++ 1. Go to your [symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) and verify your latest upload appears. ++ ++ 2. Check your production JavaScript files in browser dev tools. They should include a source map reference comment: ++ ++ JavaScript ++ ++ PostHog AI ++ ++ ```javascript ++ //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c ++ ``` ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.gitignore b/.gitignore +index a547bf3..7ceb59f 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -22,3 +22,4 @@ dist-ssr + *.njsproj + *.sln + *.sw? ++.env +diff --git a/package-lock.json b/package-lock.json +index 782aa1a..e6cb304 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -8,6 +8,7 @@ + "name": "react-vite", + "version": "0.0.0", + "dependencies": { ++ "@posthog/rollup-plugin": "^1.5.1", + "posthog-js": "^1.376.2", + "react": "^19.2.6", + "react-dom": "^19.2.6" +@@ -54,6 +55,29 @@ + "tslib": "^2.4.0" + } + }, ++ "node_modules/@jridgewell/sourcemap-codec": { ++ "version": "1.5.5", ++ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", ++ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", ++ "license": "MIT" ++ }, ++ "node_modules/@napi-rs/lzma-linux-x64-gnu": { ++ "version": "1.5.1", ++ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", ++ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true, ++ "engines": { ++ "node": "^22.20 || ^24.12 || >=25" ++ } ++ }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", +@@ -329,6 +353,49 @@ + "url": "https://github.com/sponsors/Boshen" + } + }, ++ "node_modules/@posthog/cli": { ++ "version": "0.14.1", ++ "resolved": "https://registry.npmjs.org/@posthog/cli/-/cli-0.14.1.tgz", ++ "integrity": "sha512-gTzcKpl9TZLf0LrlLHEjChlPc9LIK1gdQG0alMnX6+b+W1mTD+6nTN0W/MeEzjT4DiKDeK8FPhc1n7dT1tWKFw==", ++ "hasInstallScript": true, ++ "hasShrinkwrap": true, ++ "license": "MIT", ++ "dependencies": { ++ "detect-libc": "^2.1.2" ++ }, ++ "bin": { ++ "posthog-cli": "run-posthog-cli.js" ++ }, ++ "engines": { ++ "node": ">=14.14", ++ "npm": ">=6" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/detect-libc": { ++ "version": "2.1.2", ++ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", ++ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", ++ "license": "Apache-2.0", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/prettier": { ++ "version": "3.8.3", ++ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", ++ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", ++ "extraneous": true, ++ "license": "MIT", ++ "bin": { ++ "prettier": "bin/prettier.cjs" ++ }, ++ "engines": { ++ "node": ">=14" ++ }, ++ "funding": { ++ "url": "https://github.com/prettier/prettier?sponsor=1" ++ } ++ }, + "node_modules/@posthog/core": { + "version": "1.29.11", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.29.11.tgz", +@@ -338,6 +405,29 @@ + "@posthog/types": "1.376.2" + } + }, ++ "node_modules/@posthog/plugin-utils": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@posthog/plugin-utils/-/plugin-utils-1.2.0.tgz", ++ "integrity": "sha512-SXG2oVxPnliYKmixyIYqPv1CA4UYPZy9fQL5H+mvN/OQpKioRTawp8I2ofQmf6SfiYpnf+KAzLiRUlK7S3rOCw==", ++ "license": "MIT", ++ "dependencies": { ++ "cross-spawn": "^7.0.6" ++ } ++ }, ++ "node_modules/@posthog/rollup-plugin": { ++ "version": "1.5.1", ++ "resolved": "https://registry.npmjs.org/@posthog/rollup-plugin/-/rollup-plugin-1.5.1.tgz", ++ "integrity": "sha512-4ZNhhgnMEdRXP5YlLngHEwaaobrpNhHMT7JY4kZ1uLjGeURRbq7OjWNNg2MGN6iJgR8iIbvbt4Kho1WxflCz6A==", ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/plugin-utils": "^1.2.0", ++ "magic-string": "^0.30.17" ++ }, ++ "peerDependencies": { ++ "rollup": ">= 4.0.0" ++ } ++ }, + "node_modules/@posthog/types": { + "version": "1.376.2", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.376.2.tgz", +@@ -500,9 +590,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -520,9 +607,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -540,9 +624,6 @@ + "ppc64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -560,9 +641,6 @@ + "s390x" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -580,9 +658,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -600,9 +675,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -689,6 +761,356 @@ + "dev": true, + "license": "MIT" + }, ++ "node_modules/@rollup/rollup-android-arm-eabi": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", ++ "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", ++ "cpu": [ ++ "arm" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "android" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-android-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", ++ "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "android" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-darwin-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", ++ "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "darwin" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-darwin-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", ++ "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "darwin" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-freebsd-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", ++ "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "freebsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-freebsd-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", ++ "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "freebsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm-gnueabihf": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", ++ "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", ++ "cpu": [ ++ "arm" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm-musleabihf": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", ++ "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", ++ "cpu": [ ++ "arm" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", ++ "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", ++ "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-loong64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", ++ "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", ++ "cpu": [ ++ "loong64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-loong64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", ++ "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", ++ "cpu": [ ++ "loong64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-ppc64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", ++ "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", ++ "cpu": [ ++ "ppc64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-ppc64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", ++ "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", ++ "cpu": [ ++ "ppc64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-riscv64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", ++ "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", ++ "cpu": [ ++ "riscv64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-riscv64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", ++ "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", ++ "cpu": [ ++ "riscv64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-s390x-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", ++ "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", ++ "cpu": [ ++ "s390x" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-x64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", ++ "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-x64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", ++ "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-openbsd-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", ++ "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "openbsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-openharmony-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", ++ "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "openharmony" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-arm64-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", ++ "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", ++ "cpu": [ ++ "arm64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-ia32-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", ++ "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", ++ "cpu": [ ++ "ia32" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-x64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", ++ "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-x64-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", ++ "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", ++ "cpu": [ ++ "x64" ++ ], ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", +@@ -700,6 +1122,13 @@ + "tslib": "^2.4.0" + } + }, ++ "node_modules/@types/estree": { ++ "version": "1.0.9", ++ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", ++ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/@types/node": { + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", +@@ -773,6 +1202,20 @@ + "url": "https://opencollective.com/core-js" + } + }, ++ "node_modules/cross-spawn": { ++ "version": "7.0.6", ++ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ++ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", ++ "license": "MIT", ++ "dependencies": { ++ "path-key": "^3.1.0", ++ "shebang-command": "^2.0.0", ++ "which": "^2.0.1" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } ++ }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", +@@ -784,7 +1227,6 @@ + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", +- "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" +@@ -827,7 +1269,6 @@ + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", +- "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, +@@ -838,6 +1279,12 @@ + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, ++ "node_modules/isexe": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", ++ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", ++ "license": "ISC" ++ }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", +@@ -981,9 +1428,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1005,9 +1449,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1029,9 +1470,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1053,9 +1491,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1117,6 +1552,15 @@ + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, ++ "node_modules/magic-string": { ++ "version": "0.30.21", ++ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", ++ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", ++ "license": "MIT", ++ "dependencies": { ++ "@jridgewell/sourcemap-codec": "^1.5.5" ++ } ++ }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", +@@ -1136,6 +1580,15 @@ + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, ++ "node_modules/path-key": { ++ "version": "3.1.1", ++ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", ++ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", +@@ -1301,12 +1754,79 @@ + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, ++ "node_modules/rollup": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", ++ "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/estree": "1.0.9" ++ }, ++ "bin": { ++ "rollup": "dist/bin/rollup" ++ }, ++ "engines": { ++ "node": ">=18.0.0", ++ "npm": ">=8.0.0" ++ }, ++ "optionalDependencies": { ++ "@napi-rs/lzma-linux-x64-gnu": "1.5.1", ++ "@rollup/rollup-android-arm-eabi": "4.63.0", ++ "@rollup/rollup-android-arm64": "4.63.0", ++ "@rollup/rollup-darwin-arm64": "4.63.0", ++ "@rollup/rollup-darwin-x64": "4.63.0", ++ "@rollup/rollup-freebsd-arm64": "4.63.0", ++ "@rollup/rollup-freebsd-x64": "4.63.0", ++ "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", ++ "@rollup/rollup-linux-arm-musleabihf": "4.63.0", ++ "@rollup/rollup-linux-arm64-gnu": "4.63.0", ++ "@rollup/rollup-linux-arm64-musl": "4.63.0", ++ "@rollup/rollup-linux-loong64-gnu": "4.63.0", ++ "@rollup/rollup-linux-loong64-musl": "4.63.0", ++ "@rollup/rollup-linux-ppc64-gnu": "4.63.0", ++ "@rollup/rollup-linux-ppc64-musl": "4.63.0", ++ "@rollup/rollup-linux-riscv64-gnu": "4.63.0", ++ "@rollup/rollup-linux-riscv64-musl": "4.63.0", ++ "@rollup/rollup-linux-s390x-gnu": "4.63.0", ++ "@rollup/rollup-linux-x64-gnu": "4.63.0", ++ "@rollup/rollup-linux-x64-musl": "4.63.0", ++ "@rollup/rollup-openbsd-x64": "4.63.0", ++ "@rollup/rollup-openharmony-arm64": "4.63.0", ++ "@rollup/rollup-win32-arm64-msvc": "4.63.0", ++ "@rollup/rollup-win32-ia32-msvc": "4.63.0", ++ "@rollup/rollup-win32-x64-gnu": "4.63.0", ++ "@rollup/rollup-win32-x64-msvc": "4.63.0", ++ "fsevents": "~2.3.2" ++ } ++ }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, ++ "node_modules/shebang-command": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", ++ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", ++ "license": "MIT", ++ "dependencies": { ++ "shebang-regex": "^3.0.0" ++ }, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/shebang-regex": { ++ "version": "3.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", ++ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", +@@ -1445,6 +1965,21 @@ + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz", + "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==", + "license": "Apache-2.0" ++ }, ++ "node_modules/which": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", ++ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", ++ "license": "ISC", ++ "dependencies": { ++ "isexe": "^2.0.0" ++ }, ++ "bin": { ++ "node-which": "bin/node-which" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } + } + } + } +diff --git a/package.json b/package.json +index 6042ca5..674ece9 100644 +--- a/package.json ++++ b/package.json +@@ -9,6 +9,7 @@ + "preview": "vite preview" + }, + "dependencies": { ++ "@posthog/rollup-plugin": "^1.5.1", + "posthog-js": "^1.376.2", + "react": "^19.2.6", + "react-dom": "^19.2.6" +diff --git a/posthog-source-maps-report.md b/posthog-source-maps-report.md +new file mode 100644 +index 0000000..32acaef +--- /dev/null ++++ b/posthog-source-maps-report.md +@@ -0,0 +1,57 @@ ++# PostHog Source Map Upload — Setup Report ++ ++## What was configured ++ ++### Files changed ++ ++| File | Change | ++|------|--------| ++| `vite.config.ts` | Converted to function form; added `loadEnv` to expose `POSTHOG_*` vars to the build; added `@posthog/rollup-plugin` to inject chunk IDs and upload source maps on every production build | ++| `.env` | Created with `POSTHOG_API_KEY`, `POSTHOG_PROJECT_ID`, and `POSTHOG_HOST` | ++| `package.json` (via npm) | `@posthog/rollup-plugin` added to `devDependencies` | ++ ++### Test affordance ++ ++A temporary "Test PostHog Error Tracking" button was added to `src/App.tsx` for verification and then **reverted** — no test code remains. ++ ++## Environment variables set ++ ++| Variable | Purpose | ++|----------|---------| ++| `POSTHOG_API_KEY` | Personal API key (source map upload write access) | ++| `POSTHOG_PROJECT_ID` | PostHog project ID (`228144`) | ++| `POSTHOG_HOST` | PostHog API host (`https://us.posthog.com`) | ++ ++## Build command ++ ++``` ++npm run build ++``` ++ ++This runs `tsc -b && vite build`. The `@posthog/rollup-plugin` injects chunk IDs into the output bundles and uploads the source maps to PostHog automatically. Source map files are deleted from the output directory after upload (`deleteAfterUpload: true`). ++ ++## Run command ++ ++``` ++npm run preview ++``` ++ ++Serves the production build locally for testing. ++ ++## CI/CD — manual action required ++ ++No CI configuration was found in this project (`Dockerfile`, `.github/workflows/`, `.gitlab-ci.yml`, etc.). Wherever your production build runs, you must expose the following as environment variables before `npm run build` executes: ++ ++- `POSTHOG_API_KEY` ++- `POSTHOG_PROJECT_ID` ++- `POSTHOG_HOST` ++ ++**Do not commit `.env` to version control.** Use your CI provider's secret store (e.g. GitHub: Settings → Secrets and variables → Actions; GitLab: Settings → CI/CD → Variables) and expose them as environment variables on the build job. ++ ++## Verify the upload ++ ++After running `npm run build`, confirm the upload landed: ++ ++👉 **Symbol sets page:** https://us.posthog.com/project/228144/error_tracking/configuration ++ ++A new symbol set entry should appear after each build. Stack traces for errors captured with `posthog.captureException()` will then resolve to your original source files instead of minified bundle paths. +diff --git a/vite.config.ts b/vite.config.ts +index 8b0f57b..ce49f13 100644 +--- a/vite.config.ts ++++ b/vite.config.ts +@@ -1,7 +1,23 @@ +-import { defineConfig } from 'vite' ++import { defineConfig, loadEnv } from 'vite' + import react from '@vitejs/plugin-react' ++import posthog from '@posthog/rollup-plugin' + + // https://vite.dev/config/ +-export default defineConfig({ +- plugins: [react()], +-}) ++export default ({ mode }: { mode: string }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') } ++ ++ return defineConfig({ ++ plugins: [ ++ react(), ++ posthog({ ++ personalApiKey: process.env.POSTHOG_API_KEY!, ++ projectId: process.env.POSTHOG_PROJECT_ID, ++ host: process.env.POSTHOG_HOST, ++ sourcemaps: { ++ enabled: true, ++ deleteAfterUpload: true, ++ }, ++ }), ++ ], ++ }) ++} diff --git a/results/source-maps-sol-medium/react-vite__anthropic/result.json b/results/source-maps-sol-medium/react-vite__anthropic/result.json new file mode 100644 index 000000000..bb481e07a --- /dev/null +++ b/results/source-maps-sol-medium/react-vite__anthropic/result.json @@ -0,0 +1,24 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "@posthog/rollup-plugin", + "posthog-js" + ], + "envFile": "/tmp/sm-run-react-vite__anthropic/.env", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file diff --git a/results/source-maps-sol-medium/react-vite__sol-medium/app.diff b/results/source-maps-sol-medium/react-vite__sol-medium/app.diff new file mode 100644 index 000000000..1e2ce493d --- /dev/null +++ b/results/source-maps-sol-medium/react-vite__sol-medium/app.diff @@ -0,0 +1,1628 @@ +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/.posthog-wizard b/.claude/skills/error-tracking-upload-source-maps-vite/.posthog-wizard +new file mode 100644 +index 0000000..e69de29 +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md b/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md +new file mode 100644 +index 0000000..000cc83 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/SKILL.md +@@ -0,0 +1,408 @@ ++--- ++name: error-tracking-upload-source-maps-vite ++description: Upload source maps to PostHog Error Tracking for Vite ++metadata: ++ author: PostHog ++ version: 1.49.1 ++--- ++ ++# Upload source maps to PostHog for Vite ++ ++This skill helps you upload source maps (or platform debug symbols) so PostHog Error Tracking can resolve minified stack traces back to your original source. ++ ++## Reference files ++ ++- `references/vite.md` - Upload source maps for vite - docs ++- `references/upload-source-maps.md` - Upload source maps - docs ++- `references/cli.md` - Upload source maps with cli - docs ++- `references/COMMANDMENTS.md` - Framework-specific rules the integration must follow ++ ++The overview lists every supported framework and build tool. The CLI reference covers `posthog-cli sourcemap process`, which injects chunk IDs and uploads maps in one step. Native binaries (Go, Rust) instead use `posthog-cli symbol-sets upload` — it uploads debug symbols discovered in a build directory, with no inject step; the platform reference covers it. ++ ++## Steps ++ ++The stages of wiring up source map upload, in order. Each step has a short overview, gotchas under **Tips**, and per-technology notes under **Examples**. The reference files above are the source of truth for the exact, per-framework API — when this page and a reference disagree, follow the reference for Vite. ++ ++### Get a personal API key ++ ++Source map upload authenticates with a **personal API key**, not the public project API key the SDK uses at runtime. The key needs error-tracking write access; the quickest path is the "Source map upload" preset on PostHog's personal API keys settings page. ++ ++#### Tips ++- The public project key (the one in your SDK `init`) will **not** work for uploads — it has no write scope for symbol sets. ++- Never hardcode the key in source. It belongs in an environment variable read at build time (see "Write credentials to the env file"). ++- Keys can't be minted programmatically — create them by hand in PostHog settings, then store the value as a secret. ++ ++### Apply build-config changes ++ ++Wire source map generation, chunk-ID injection, and upload into your **production build** so every deploy ships matching maps. Depending on the platform this is either a build/bundler plugin, or a `posthog-cli sourcemap process` step run after the build (it injects chunk IDs and uploads in one pass). Follow the Vite reference for the exact wiring. ++ ++#### Tips ++- If you wire `posthog-cli` directly (no framework or bundler plugin), generating the maps is **your** responsibility — the CLI only injects chunk IDs into, and uploads, maps your build already produced. Two things must be true before `posthog-cli sourcemap process` works: ++ - Source maps are emitted next to your output bundles (e.g. `.js.map` files). ++ - The maps include `sourcesContent` (the original source embedded inside the map). Without it PostHog has the line/column mappings but not the code, so traces can't be fully resolved. ++- **Inject before deploy**: the *injected* bundles must be the ones shipped to production. Bundles missing the `//# chunkId=…` comment can't be matched to uploaded maps. ++- Wire injection + upload into the build itself (plugin, post-build script, or CI step) — manual uploads drift from deployed code. ++- **Don't ship source maps publicly**: omit `.map` files from the deployed artifact, or use hidden source maps. Uploaded maps live in PostHog, not on your origin. ++- **Link each release to its commit.** The CLI auto-detects the commit from the CI's git env vars — see "Associate the release with a git commit" for making those reachable in Docker/CI builds. ++ ++#### Examples ++- **Node / tsc** Emit maps with embedded sources by setting both in `tsconfig.json`: `"sourceMap": true` and `"inlineSources": true`. Then run `posthog-cli sourcemap process` against the build output dir as a post-build step — it injects chunk IDs and uploads in one pass, and needs the upload credentials (see "Make credentials available at build time"). ++- **Vite / Webpack / Rollup** Prefer the bundler plugin from the reference over hand-rolling the CLI — it injects and uploads in one pass. Make sure the bundler is configured to emit source maps. ++- **iOS (Xcode)** iOS uploads **dSYM debug symbols**, not source maps. Required target changes: ++ 1. `DEBUG_INFORMATION_FORMAT = dwarf-with-dsym` for Release. ++ 2. `ENABLE_USER_SCRIPT_SANDBOXING = NO`. ++ 3. A Run Script phase, ordered last, with `$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Resources/DWARF/$(EXECUTABLE_NAME)` in its Input Files, calling the SDK's bundled script — do not hand-roll the upload: ++ - SPM: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${BUILD_DIR%/Build/*}/SourcePackages/checkouts/posthog-ios/build-tools/upload-symbols.sh"` ++ - CocoaPods: `POSTHOG_INCLUDE_SOURCE=1 POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env" "${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh"` ++ Copy the invocation verbatim — the `POSTHOG_INCLUDE_SOURCE=1` and `POSTHOG_CLI_DOTENV_FILE` prefixes HAVE to be there. This needs a recent `posthog-cli` (older ones silently ignore `POSTHOG_CLI_DOTENV_FILE`); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++- **Android (Gradle)** Android uploads **ProGuard/R8 mapping files**, not source maps. Apply the `com.posthog.android` Gradle plugin on the **app module's** `build.gradle(.kts)` (never the root project), per the reference — the plugin hooks the build and uploads automatically, do not hand-roll a `posthog-cli` step. Gotchas: ++ 1. The plugin only hooks minified variants — if the release build type has `isMinifyEnabled = false`, set it to `true` (keep the existing `proguardFiles` line) or nothing is uploaded. ++ 2. The upload shells out to `posthog-cli` on the `PATH` (v0.7.4+); the PostHog wizard installs it for you, so do not run `npm install -g` yourself. ++ 3. The Gradle plugin is versioned separately from the `posthog-android` SDK — never reuse the SDK version in `id("com.posthog.android") version "…"`. ++- **Go** Go uploads **native debug symbols**, not source maps, and there is no inject step — the binary's identity (GNU build ID on Linux, Mach-O UUID on macOS) links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory ` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — every build gets its own identity, so re-upload for each deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. On Linux, Go emits no GNU build ID by default — build with `go build -ldflags="-B gobuildid"`. The flag matters at runtime too, not only for upload: without it the SDK can't identify the running binary and falls back to plain runtime-resolved frames. ++ 2. On macOS, disable DWARF compression instead: `go build -ldflags="-compressdwarf=false"` — symbolication can't read the compressed form (the Mach-O UUID identity is automatic). ++ 3. Never build with `-ldflags="-s"` or `-ldflags="-w"` (they strip the DWARF, leaving nothing to upload), and avoid `-trimpath` (it rewrites the source paths `--include-source` reads from). ++ 4. Requires posthog-go 1.22.0+ — older SDKs never emit the instruction addresses and `$debug_images` server-side symbolication needs, so uploaded symbols would sit unused. If go.mod pins an older version, upgrade it as part of this step: `go get github.com/posthog/posthog-go@latest && go mod tidy`. ++ 5. Windows binaries aren't supported yet — the SDK falls back to plain runtime frames there. ++- **Rust (Cargo)** Rust uploads **native debug symbols**, not source maps, and there is no inject step — the build ID baked into the binary links frames to the uploaded symbols. The upload is a standalone CLI step after the build: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` (add `--include-source` so PostHog can show source context around frames). Wire it into the same script/pipeline that produces the production binary — each build has its own build ID, so symbols must be re-uploaded for every deployed build. The wizard pre-installs `posthog-cli` for you, so do not run `npm install -g` yourself. Gotchas: ++ 1. Release builds omit debug info by default — set `debug = "line-tables-only"` under `[profile.release]` in `Cargo.toml` (enough for file, line, and inline resolution), per the reference. ++ 2. On macOS also set `split-debuginfo = "packed"` in the same profile — the default leaves debug info in intermediate object files and no `.dSYM` bundle is produced for the CLI to upload. ++ 3. If the profile sets `strip` explicitly, set it to `"none"` — a stripped binary leaves nothing to upload. ++ 4. In a Cargo **workspace**, `[profile.*]` settings are only honored in the workspace root `Cargo.toml` — put the debug-info profile there, not in a member crate — and the build output is the workspace-level `target/release`, so point the upload `--directory` at that. Resolve the root with `cargo locate-project --workspace --message-format plain` (prints the root manifest path); the gitignored `.env` belongs next to that root manifest too. ++- **Next.js / Nuxt / Angular** Use the framework's documented source-map upload integration from the reference; these own their build pipeline, so configure upload there rather than bolting on a separate CLI step. ++- **React Native (Expo)** Per the reference: add the `posthog-react-native/expo` plugin entry to `plugins` in `app.json`, and switch `metro.config.js` to `getPostHogExpoConfig` from `posthog-react-native/metro`. The reference badges **native crash symbolication** as *optional* — here it is not: enable `uploadNativeSymbols` with source inclusion on the plugin entry. ++ Gotchas: ++ 1. The PostHog wizard installs `posthog-cli` for you — do not run `npm install -g` yourself. ++ 2. You **must** also enable native crash autocapture (`errorTracking.autocapture.nativeCrashes`) in the SDK setup and install the `@posthog/react-native-plugin` package it depends on — per the reference. ++- **Flutter** One upload path per platform directory present (`web/`, `android/`, `ios/`) — wire every one that exists. There is no Dart-level upload. ++ - **Web** `flutter build web --source-maps`, then `posthog-cli sourcemap process --directory build/web` as a post-build step. ++ - **Android** Follow the **Android (Gradle)** bullet above, but on `android/app/build.gradle.kts` (never `android/build.gradle.kts`). Flutter's `android/settings.gradle.kts` owns plugin versions: declare `id("com.posthog.android") version "" apply false` there, then apply it versionless in the app module. Skip that bullet's `isMinifyEnabled` step — Flutter always shrinks release builds. ++ - **iOS** Follow the **iOS (Xcode)** bullet above, on the **Runner** target in `ios/Runner.xcworkspace`. Flutter is always CocoaPods: `${PODS_ROOT}/PostHog/build-tools/upload-symbols.sh`. ++ ++ Set `captureNativeExceptions = true` in `PostHogConfig.errorTrackingConfig` — it defaults to `false`, and while it's off the native SDKs capture nothing to symbolicate. ++ ++### Make credentials available at build time ++ ++The upload credentials must be readable **by the build pipeline at build time**, not merely present in a `.env` file. Whether `.env` is auto-loaded depends on the technology. ++ ++#### Tips ++- **Auto-loads `.env`**: Next.js, Nuxt and similar frameworks read `.env` into the build for you — nothing extra to do. ++- **Vite is a partial exception**: it auto-loads `.env` into `import.meta.env` for client code (only `VITE_`-prefixed vars), but does **not** put vars in `process.env` for your config to read. The upload credentials (`POSTHOG_*`, not `VITE_`-prefixed) are read when the plugin is constructed, so load them yourself — see the Vite example below. ++- **Does NOT auto-load `.env`**: Rollup, plain webpack, and plain Node scripts. Load it explicitly — add `dotenv` (`require('dotenv').config()`, or `import 'dotenv/config'` for ESM) at the top of the bundler/config file. ++- **Separate-process gotcha**: if `posthog-cli sourcemap process` runs as its own `package.json` step (after the bundler), the CLI call is a **separate child process** and will *not* see env vars a loader set inside the bundler config. Point the CLI at the file directly: `posthog-cli --dotenv-file sourcemap process …` (the flag goes before the subcommand). ++- **`process` authenticates from the start.** `posthog-cli sourcemap process` resolves credentials before it injects chunk IDs — the inject phase needs them too, not just the upload — and fails without them. Always pass `--dotenv-file` to the `process` invocation. (It can still appear to work if the developer once ran `posthog-cli login`, which leaves credentials in `~/.posthog` — that won't exist in CI or on a teammate's machine.) ++- **iOS / Xcode** No loader — the Run Script phase's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix points posthog-cli at the gitignored `.env`. `POSTHOG_CLI_HOST` is the API host (`https://us.posthog.com`), never the `*.i.posthog.com` ingestion host. ++- **Android / Gradle** Gradle does not read `.env` — bridge it in the app module's build script (see the Android example). Unset properties fall back to real `POSTHOG_CLI_*` environment variables, so the same wiring works in CI. The host var follows the same API-host rule as iOS above. ++- **React Native (Expo)** Add `"dotenvFile": ".env"` to the `posthog-react-native/expo` plugin entry's options in `app.json` (needs posthog-react-native >= 4.60.0 — bump the package if older). No Xcode or Gradle wiring needed — the plugin handles the native hooks. In CI, set the `POSTHOG_CLI_*` values as job secrets instead. The host var follows the same API-host rule as iOS above. ++- **Flutter** One gitignored `.env` at the Flutter project root. Both native sub-projects sit one level down, so they reach *up* for it: ++ - Web: `posthog-cli --dotenv-file .env sourcemap process --directory build/web` (flag goes **before** the subcommand). ++ - Android: `rootProject.file("../.env")` — Gradle's root project is `android/`, not the Flutter root. ++ - iOS: `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/../.env"` — `SRCROOT` is `ios/`. ++- **Go / Rust** The upload is always a standalone `posthog-cli` step after the compiler runs, so the separate-process rule applies — pass the dotenv file explicitly (flag before the subcommand): `posthog-cli --dotenv-file .env symbol-sets upload --directory `. The host var follows the same API-host rule as iOS above. ++ ++#### Examples ++- **Next.js / Nuxt** Auto-load `.env` at build time; put the vars there and you're done. ++- **Vite** Export `vite.config` as a function and merge `loadEnv` into `process.env` so the config (and the PostHog plugin) can read the upload credentials. Pass `''` as the third arg so non-`VITE_` vars like `POSTHOG_API_KEY` are included — the default `'VITE_'` prefix skips them: ++ ```ts ++ import { defineConfig, loadEnv } from 'vite'; ++ ++ export default ({ mode }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') }; ++ // process.env.POSTHOG_API_KEY is now readable by the plugins below ++ return defineConfig({ ++ plugins: [/* … posthog source map plugin … */], ++ }); ++ }; ++ ``` ++- **Rollup / webpack / plain Node** Add `import 'dotenv/config'` (or `require('dotenv').config()`) at the top of the config/entry file so the loader runs before the build reads the vars. ++- **Standalone posthog-cli step** Pass `--dotenv-file .env` to the `process` invocation so it can authenticate: ++ ```json ++ "build": "tsc && posthog-cli --dotenv-file .env sourcemap process --directory ./dist --release-name my-app" ++ ``` ++- **iOS (Xcode / posthog-cli)** A gitignored `.env` next to the `.xcodeproj` — the Run Script invocation's `POSTHOG_CLI_DOTENV_FILE="${SRCROOT}/.env"` prefix hands it to posthog-cli. No Xcode project wiring beyond the Run Script phase. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Android (Gradle / posthog-cli)** A gitignored `.env` at the Gradle project root, bridged into the upload tasks in the **app module's** `build.gradle.kts`: ++ ```kotlin ++ import com.posthog.android.PostHogCliExecTask ++ import java.util.Properties ++ ++ val postHogEnv = Properties().apply { ++ val envFile = rootProject.file(".env") ++ if (envFile.exists()) envFile.inputStream().use { load(it) } ++ } ++ ++ tasks.withType().configureEach { ++ postHogEnv.getProperty("POSTHOG_CLI_API_KEY")?.let { postHogApiKey.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_PROJECT_ID")?.let { postHogProjectId.set(it) } ++ postHogEnv.getProperty("POSTHOG_CLI_HOST")?.let { postHogHost.set(it) } ++ } ++ ``` ++ (Groovy `build.gradle`: same shape with `tasks.withType(PostHogCliExecTask).configureEach { … }`.) In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner. ++- **Go (posthog-cli)** A gitignored `.env` at the module root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory `. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; the build itself does not need the credentials. ++- **Rust (Cargo / posthog-cli)** A gitignored `.env` at the crate root, passed straight to the CLI: `posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`. In CI, set the `POSTHOG_CLI_*` values as job secrets instead — no `.env` on the runner — and scope them to the upload step only; `cargo build` runs dependency build scripts and does not need the credentials. ++ ++### Write credentials to the env file ++ ++Write the personal API key and project identifiers into the env file your build reads. Reuse the file the project already uses — don't introduce a second one. ++ ++#### Tips ++- Picking the file: if an env file already contains PostHog vars (`POSTHOG_*` / `NEXT_PUBLIC_POSTHOG_*`), use that one. Otherwise, if exactly one env file exists use it; if several exist prefer `.env`. Only create a new file when none exists. ++- Variable names depend on which uploader you wired: ++ - `posthog-cli` direct upload → `POSTHOG_CLI_API_KEY`, `POSTHOG_CLI_PROJECT_ID`, `POSTHOG_CLI_HOST` ++ - bundler-plugin variants → `POSTHOG_API_KEY`, `POSTHOG_PROJECT_ID`, `POSTHOG_HOST` ++- Set the `*_HOST` var when you're not on US Cloud's default (e.g. EU Cloud or self-hosted); setting it explicitly always is safe. Follow the reference for the variant. ++- In CI/CD, set the same vars as secrets — never commit the key. ++ ++### Identify the build and run commands ++ ++Resolve two concrete commands for this project: the production **build** command (the one that uploads source maps) and the **run** command that launches the built app (so a test error can be triggered against the real artifact). ++ ++#### Tips ++- Resolve real commands from the project's actual scripts/config — substitute the correct package manager. Never leave a generic "start the app". ++- When a build artifact is involved, prefer the command that serves the *production* build over the dev server. ++ ++#### Examples ++- **Next.js** Build: `npm run build` (`next build`). Run: `npm run start` (`next start`). ++- **Vite** Build: `npm run build`. Run: `npm run preview`. ++- **Plain Node** Build: `npm run build`. Run: `node ` — read package.json `main`/`bin` and the build output dir to name the real file (e.g. `node dist/index.js`). ++- **Android** Build: `./gradlew assembleRelease`. Run: launch on a device/emulator (Android Studio, or `./gradlew installRelease`). ++- **iOS** Local build + run are one step: Xcode Run with Build Configuration = Release. `xcodebuild` is CI-only. ++- **React Native (Expo)** Build + run are one step per platform: `npx expo run:ios --configuration Release` / `npx expo run:android --variant release`. ++- **Go** Build: `go build -ldflags="-B gobuildid" -o bin/ . && posthog-cli --dotenv-file .env symbol-sets upload --directory ./bin` (macOS: `-ldflags="-compressdwarf=false"` instead) — the upload is a separate CLI step, so the resolved build command must include it (use the project's Makefile/script target instead when you wired the upload into one). Run: `./bin/`. ++- **Rust** Build: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release` — the upload is a separate CLI step, so the resolved build command must include it (use the project's build script/Makefile target instead when you wired the upload into one). Run: `./target/release/` — read the binary name from `Cargo.toml` (the `[package]` name, or a `[[bin]]` entry). ++- **Flutter** One pair per platform you wired: ++ - Web — Build: `flutter build web --source-maps`. Run: `python3 -m http.server 8000 --directory build/web`. Not `flutter run -d chrome` — the dev server skips the upload. ++ - Android — Build: `flutter build apk --release`. Run: `flutter run --release`. ++ - iOS — Build: `flutter build ipa`. Run: `flutter run --release`. ++ ++### Set up CI for automatic uploads ++ ++Source maps are only uploaded when the **production build** runs, so the environment that builds and deploys your app needs the same upload credentials you put in the env file. The whole job is: **find where the production build command actually runs, then make the upload credentials reachable at that exact spot.** **Only ever edit CI/deploy files that already exist — never create a new workflow, pipeline, or deploy file.** Wiring credentials means modifying the build/deploy config this project already has; it is never license to author new CI. The build is where maps inject + upload, and env does **not** automatically cross three boundaries — into a Docker build, into a nested/composite action, or into an SSH session. So trace the deploy path before editing anything: ++ ++1. Is there a `Dockerfile`? If the build command runs inside it (`RUN `), the build happens in that image's **build stage**. ++2. Is there a workflow under `.github/workflows/`? Open it and find the step that triggers the build, then follow it to where the build truly executes — it may be: ++ - an inline build step (`run: npm run build`) on the runner, ++ - a `docker build` / `docker/build-push-action` step (build runs in the image), ++ - a `uses: ./.github/actions/...` **local composite action** — open that `action.yml`; the real build step is one layer down, ++ - an `ssh`/deploy step (e.g. `appleboy/ssh-action`) whose `script:` runs the build **on a remote server**. ++3. Any other CI config in the repo (`.gitlab-ci.yml`, `.circleci/config.yml`, `Jenkinsfile`, `bitbucket-pipelines.yml`, `azure-pipelines.yml`, …)? Open it and find the job/stage that runs the production build. The principle is identical; apply it with your working knowledge of that provider — the examples below show the pattern to mirror. ++4. No `Dockerfile`, no CI config, no build step you can trace? Don't guess — tell the user where the creds need to be (see "Untraceable setup" under Examples). ++ ++#### Tips ++- **A deploy file for another package is not license to author one for this one.** In a monorepo especially, finding a workflow that deploys a *sibling* package (e.g. a `deploy-backend.yml`, or a `Dockerfile`/pipeline for another app) does **not** mean you should create a matching `deploy-frontend.yml` (or any new CI file) for the project you're instrumenting. Wire credentials only into the existing file that builds *this* project. If this project has no build/deploy config you can open and edit, it is untraceable: make no CI changes and hand the requirement to the user (see "Untraceable setup") — do **not** invent one. ++- Reuse the **exact variable names** from "Write credentials to the env file" — the build reads the same names locally and in CI. (`POSTHOG_CLI_*` for direct `posthog-cli`; `POSTHOG_*` for bundler-plugin uploaders.) ++- **In CI, credentials travel as environment variables — never as a file.** Do not materialize a `.env` on the runner (e.g. `printf … > .env` before the build), and never copy or un-ignore one into a Docker image: it's redundant, and a secrets file on disk can leak into artifacts, caches, or image layers. A build script that passes `--dotenv-file .env` to `posthog-cli` works unchanged in CI even though `.env` doesn't exist there: real environment variables take precedence over the file, and a missing file is skipped with a warning. ++- **Never commit secret values.** Reference credentials by name only: Docker `ARG`/`ENV` or BuildKit secret ids, `${{ secrets.* }}` in GitHub Actions. The personal API key stays out of version control. ++- Layers stack — a workflow can call a composite action that runs `docker build` against a Dockerfile. Wire **every** layer the credentials must pass through, from the outer `${{ secrets.* }}` reference down to the `ARG`/`ENV` in the build stage. ++- **Multi-stage Dockerfiles:** put the `ARG`/`ENV` in the **build stage** (where the build command runs), never the runtime stage. That's both correct (the build needs them) and safer (the creds don't get baked into the shipped image). ++- **Single-stage Dockerfiles:** with no separate build stage, `ARG`/`ENV` would bake the API key into the shipped image (`docker inspect` reveals `ENV`; `docker history` can reveal build args). Mount the key as a **BuildKit secret** on the build `RUN` instead — it exists for that command only and is never written to a layer (see the single-stage example). Plain `ARG`/`ENV` stays fine for the non-secret project ID and host. ++- **Composite / reusable actions can't read `secrets`.** Inside a `.github/actions/*/action.yml` only `${{ inputs.* }}` is available. Add an `inputs:` entry per credential, reference `${{ inputs.* }}` there, and pass `${{ secrets.* }}` from the calling workflow's `with:` block. ++- **Build over SSH:** the runner's env doesn't reach the remote box. Set the vars inline immediately before the build command inside the `script:`. `${{ secrets.* }}` is substituted by Actions *before* the script is sent, so the value travels with the script. ++- **The worked examples are exemplars, not an allowlist.** For any provider not shown (GitLab CI, CircleCI, Jenkins, Bitbucket, Azure Pipelines, …), apply the same principle with your knowledge of that provider: find the job that runs the production build, expose the credentials there via the provider's native secret mechanism (GitLab project CI/CD variables, CircleCI project env vars / contexts, Jenkins credentials + `withCredentials`, …), and cross the same boundaries the same way — Docker builds still need `--build-arg`, SSH sessions still need inline vars. ++- **Make only the edits the provider actually needs.** Some providers inject project-level variables straight into every job's environment — GitLab CI/CD variables work this way — so an inline build step may need **no functional pipeline change at all**. When that's the conclusion, still add a short comment on the build job naming the required variables and where to create them (see the GitLab example) — the requirement must be visible in the repo, not only in your hand-off — and tell the user exactly which variables to create and where. ++- You can't create CI secrets. Whenever the pipeline reads a credential, tell the user where to add it before their next deploy — GitHub: **Settings → Secrets and variables → Actions**; GitLab: **Settings → CI/CD → Variables**; other providers: their equivalent secret store. The pipeline can't read a secret that doesn't exist yet. ++ ++#### Examples ++- **Dockerfile build stage (e.g. `Dockerfile`, no CI)** Declare the credentials as build args and promote them to env vars *before* the build `RUN`, in the build stage: ++ ```dockerfile ++ FROM node:22-slim AS build ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_API_KEY ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_API_KEY=$POSTHOG_CLI_API_KEY \ ++ POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN npm run build # now sees the upload credentials ++ ``` ++ With no CI wiring the image, tell the user to pass them when they build: `docker build --build-arg POSTHOG_CLI_API_KEY=… --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .` ++- **Single-stage Dockerfile (BuildKit secret)** When build and runtime share one stage, pass the API key as a BuildKit secret so it never lands in the image; keep `ARG`/`ENV` for the non-secret project ID and host: ++ ```dockerfile ++ # syntax=docker/dockerfile:1 ++ FROM node:22-slim ++ WORKDIR /app ++ # ... ++ ARG POSTHOG_CLI_PROJECT_ID ++ ARG POSTHOG_CLI_HOST ++ ENV POSTHOG_CLI_PROJECT_ID=$POSTHOG_CLI_PROJECT_ID \ ++ POSTHOG_CLI_HOST=$POSTHOG_CLI_HOST ++ RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY \ ++ npm run build ++ ``` ++ Build with `docker build --secret id=POSTHOG_CLI_API_KEY,env=POSTHOG_CLI_API_KEY --build-arg POSTHOG_CLI_PROJECT_ID=… --build-arg POSTHOG_CLI_HOST=… .`. In `docker/build-push-action`, pass the key through the `secrets:` input (`POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }}`) instead of `build-args:`. The `env=` attribute on `--mount` needs a current BuildKit — keep the `# syntax=docker/dockerfile:1` line; on engines too old for it, read the file form instead: `RUN --mount=type=secret,id=POSTHOG_CLI_API_KEY POSTHOG_CLI_API_KEY=$(cat /run/secrets/POSTHOG_CLI_API_KEY) npm run build`. ++- **GitHub Actions — inline build step** Build runs on the runner; expose the creds with `env:` on that step: ++ ```yaml ++ - name: Build ++ run: npm run build ++ env: ++ POSTHOG_CLI_API_KEY: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — `docker build` / `docker/build-push-action`** Add the `ARG`/`ENV` to the Dockerfile build stage (above), then forward the creds as build args. Raw `docker build` takes `--build-arg`; `docker/build-push-action` takes a multi-line `build-args:` input — **merge into the existing `with:` block, don't add a second step**: ++ ```yaml ++ - name: Build and push image ++ uses: docker/build-push-action@v6 ++ with: ++ context: . ++ file: Dockerfile ++ push: true ++ tags: ${{ steps.meta.outputs.tags }} ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ secrets.POSTHOG_CLI_API_KEY }} ++ POSTHOG_CLI_PROJECT_ID=${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ POSTHOG_CLI_HOST=${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — nested/composite action** When the workflow delegates the build with `uses: ./.github/actions/build-and-push`, the `build-push-action` lives in that action's `action.yml`, which can't see `secrets`. Thread them through as inputs. In `.github/actions/build-and-push/action.yml`: ++ ```yaml ++ inputs: ++ posthog-cli-api-key: ++ required: true ++ posthog-cli-project-id: ++ required: true ++ posthog-cli-host: ++ required: true ++ runs: ++ using: composite ++ steps: ++ - uses: docker/build-push-action@v6 ++ with: ++ # ...existing context/file/push/tags... ++ build-args: | ++ POSTHOG_CLI_API_KEY=${{ inputs.posthog-cli-api-key }} ++ POSTHOG_CLI_PROJECT_ID=${{ inputs.posthog-cli-project-id }} ++ POSTHOG_CLI_HOST=${{ inputs.posthog-cli-host }} ++ ``` ++ Then pass the secrets from the calling workflow's `with:` block: ++ ```yaml ++ - uses: ./.github/actions/build-and-push ++ with: ++ # ...existing inputs... ++ posthog-cli-api-key: ${{ secrets.POSTHOG_CLI_API_KEY }} ++ posthog-cli-project-id: ${{ secrets.POSTHOG_CLI_PROJECT_ID }} ++ posthog-cli-host: ${{ secrets.POSTHOG_CLI_HOST }} ++ ``` ++- **GitHub Actions — build over SSH** When a step SSHes into a server and runs the build there (e.g. `appleboy/ssh-action` with `git pull && npm run build`), set the vars inline right before the build command inside the `script:` — mirror however the script already passes runtime vars: ++ ```yaml ++ - uses: appleboy/ssh-action@v1 ++ with: ++ host: ${{ secrets.DEPLOY_HOST }} ++ # ... ++ script: | ++ cd /srv/app && git pull --ff-only origin main && npm ci ++ POSTHOG_CLI_API_KEY="${{ secrets.POSTHOG_CLI_API_KEY }}" \ ++ POSTHOG_CLI_PROJECT_ID="${{ secrets.POSTHOG_CLI_PROJECT_ID }}" \ ++ POSTHOG_CLI_HOST="${{ secrets.POSTHOG_CLI_HOST }}" \ ++ npm run build ++ ``` ++- **GitLab CI (`.gitlab-ci.yml`)** Project CI/CD variables are injected into every job's environment automatically, so a job that runs the build inline (`script: - npm run build`) needs **no functional YAML change** — no `variables:` block, and do NOT add a script line that writes the variables into a `.env` file (`printf … > .env`, `echo … >> .env`, etc.); the build already sees them as environment variables, which take precedence over any dotenv file. DO leave a comment on the build job so the requirement is visible in the repo, not only in your hand-off: ++ ```yaml ++ build: ++ stage: build ++ # PostHog source map upload: this job needs POSTHOG_CLI_API_KEY, ++ # POSTHOG_CLI_PROJECT_ID and POSTHOG_CLI_HOST available as CI/CD ++ # variables (Settings → CI/CD → Variables); GitLab injects them into ++ # the job automatically. Mark them Masked — but Protected only if this ++ # job runs exclusively on protected branches, otherwise feature-branch ++ # builds fail with missing credentials. ++ script: ++ - npm ci ++ - npm run build ++ ``` ++ Then tell the user to add those variables in **Settings → CI/CD → Variables** and the next pipeline picks them up. Edits beyond the comment are only needed when a boundary is crossed: a job that runs `docker build` must forward them (`--build-arg POSTHOG_CLI_API_KEY="$POSTHOG_CLI_API_KEY" …`) into the Dockerfile's build stage (see the Dockerfile example), and a job that builds over SSH must set them inline before the remote build command, exactly like the SSH example above. ++- **Other CI providers (CircleCI, Jenkins, Bitbucket, Azure Pipelines, …)** Same recipe, provider-native mechanics: open the pipeline config, find the job that runs the production build, expose the credentials to that job via the provider's secret store, and thread them through any Docker/SSH boundary just like the examples above. Reference credentials by name only, then tell the user each secret to create and exactly where in the provider's UI it goes. ++- **Untraceable setup** No `Dockerfile`, no CI config, and no build step you can trace: make no CI changes — do **not** author a new workflow, pipeline, or deploy file to fill the gap. Tell the user that wherever their production build command runs, it must have the upload credentials (`POSTHOG_CLI_*` / `POSTHOG_*`) available as environment variables, or maps won't upload on deploy. If part of the path is still recognisable — e.g. a `Dockerfile` built by an unfamiliar CI — wire the layers you do recognise and tell the user exactly what the remaining layer must pass in (e.g. the `--build-arg` flags). ++ ++### Associate the release with a git commit ++ ++`posthog-cli` links the release to a **git commit, branch and repo** so Error Tracking can show which deploy an error came from. It auto-detects that from the CI's git env vars or a local `.git` directory — you never touch the CLI invocation itself (it's usually baked into `npm run build` or a bundler plugin), you just make the git context available in the build environment. A `docker build` is where this breaks: it sees **neither** the env vars nor `.git` (the same boundary credentials hit), so the release ends up linked to nothing unless you forward the vars in. ++ ++#### Tips ++- **Forward GitHub's git env vars into the Docker build** the same way you forwarded credentials. Declare each as an `ARG` **and** promote it to `ENV` — `ARG` alone isn't visible to the CLI's env lookup. That's all auto-detection needs; no CLI flags, no `.git`. ++ ++#### Examples ++- **GitHub Actions → docker build** Forward GitHub's git vars into the build stage and the CLI auto-detects branch + repo + commit: ++ ```yaml ++ build-args: | ++ GITHUB_ACTIONS=true ++ GITHUB_SHA=${{ github.sha }} ++ GITHUB_REF_NAME=${{ github.ref_name }} ++ GITHUB_REPOSITORY=${{ github.repository }} ++ GITHUB_SERVER_URL=${{ github.server_url }} ++ ``` ++ Then in the build stage, declare each as `ARG` and re-export it as `ENV` before the build runs. ++- **Inline CI build (no Docker)** GitHub Actions already sets these vars on the runner, so auto-detection just works — nothing to pass. ++ ++### Test the local setup ++ ++Optionally add a temporary, clearly-labeled affordance that captures one test exception, so you can confirm errors arrive in Error Tracking with a source-resolved stack trace after the next production build. Always remove it afterwards. ++ ++#### Tips ++- The handler must call the SDK's exception-capture method **directly** — do **not** `throw`. Throwing depends on the global error handler and shows a dev overlay; a direct capture is deterministic across platforms. ++- Pass a single Error (or platform-equivalent throwable). No custom message beyond the Error, no extra properties, no second argument — the Error's stack trace is what gets resolved. ++- Use distinctive copy on the trigger (button label / route path) so the resulting event is easy to find in the UI. ++- Read any file before editing it and capture its exact contents; after testing, restore every file the affordance touched — the affordance only, leave the upload and credential wiring in place — and re-read to confirm nothing is left behind. Never leave the affordance in place — even if the test "didn't work", revert first. ++- The upload only happens on the *production build*: build, run, trigger the error, then confirm the stack trace in Error Tracking points at real source files, not minified bundle paths. ++ ++#### Examples ++- **Browser / SPA / SSR (web, react, nextjs, nuxt, angular, vite, webpack, rollup)** Add a button such as "Test PostHog Error Tracking" on the home/root page whose onClick calls `posthog.captureException(new Error("PostHog source maps test"))`. ++- **Node.js** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that calls `posthog.captureException(new Error("PostHog source maps test"))` and returns 200. With no HTTP layer, add the capture to the existing entry script where the client is initialised rather than creating a new file. Tell the user the exact command/URL to hit. ++- **React Native** Add a visible `Button` on the main screen whose onPress calls `posthog.captureException(new Error("PostHog source maps test"))`. Test flow — the upload only runs on the **Release** build: use the Release run command from "Identify the build and run commands", launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **Android (Kotlin)** Add a `Button` on the launcher Activity whose onClick handler is exactly: ++ ```kotlin ++ import com.posthog.PostHog ++ ++ PostHog.captureException(Throwable("PostHog source maps test")) ++ ``` ++ Test flow — the upload only runs on the **minified release variant**: `./gradlew installRelease` (or Android Studio ▸ Build Variants ▸ release, then Run), launch the app, tap the button. It's an event, not a crash — the app keeps running. ++- **iOS (Swift)** `Button` on the root view (SwiftUI) or `UIButton` on the root view controller (UIKit), handler: ++ ```swift ++ do { ++ throw NSError(domain: "PostHogSourceMapTest", code: 1, ++ userInfo: [NSLocalizedDescriptionKey: "Source map upload test error"]) ++ } catch { ++ PostHogSDK.shared.captureException(error) ++ } ++ ``` ++ (`capture()` takes an event-name String, not an Error.) Test flow — give the user these steps verbatim, everything happens in Xcode (no `xcodebuild`): 1) In Xcode: Edit Scheme ▸ Run ▸ Build Configuration ▸ Release, then Run — the Release build uploads dSYMs automatically. 2) Tap the "" button in the app. It's an event, not a crash — no debugger-detach or relaunch steps. ++- **Flutter** Add an `ElevatedButton` on the home widget whose onPressed calls `Posthog().captureException(error: Exception("PostHog source maps test"), stackTrace: StackTrace.current)` — arguments are **named**, and `stackTrace` is what the trace resolves against. Give the user a test flow for **every** platform wired, using that platform's build/run pair. ++- **Go** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```go ++ client.Enqueue(posthog.NewDefaultException( ++ time.Now(), "test_user", "TestError", "PostHog source maps test", ++ )) ++ ``` ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly, then run the binary and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the binary's identity, so after any rebuild, re-upload before testing. ++- **Rust** Add a temporary route (e.g. `GET /__posthog-test-error`) on the existing server that captures one error and returns 200; with no HTTP layer, add the capture where the client is initialised. The capture is: ++ ```rust ++ let error = std::io::Error::new(std::io::ErrorKind::Other, "PostHog source maps test"); ++ client.capture_exception(&error).await.unwrap(); ++ ``` ++ Mirror how the project already calls the client: with the blocking client (`default-features = false` with `features = ["error-tracking"]` added back), drop the `.await`. ++ Test flow — the binary you run must be the one whose symbols were uploaded. Use the wired build-and-upload script if one exists; otherwise run both steps explicitly: `cargo build --release && posthog-cli --dotenv-file .env symbol-sets upload --directory target/release`, then run `./target/release/` and trigger the capture. It's an event, not a crash — the process keeps running. A rebuild changes the build ID, so after any rebuild, re-upload before testing. ++ ++### Verify and hand off ++ ++Confirm the upload landed and report what changed. ++ ++#### Tips ++- Source maps upload during the **production build** — the build must actually run for a symbol set to appear. ++- Verify in PostHog Error Tracking settings on the **Symbol sets** page: a new symbol set should appear after the build completes. ++- When handing off, list the files you edited (paths only), the env-var **key** names you set (never values), whether a test affordance was added and reverted, and the exact build command to run. ++- If you wired CI, list the pipeline files you changed (`Dockerfile`, workflow, pipeline config) and spell out every manual follow-up — e.g. the secrets the user must add in their CI provider's settings before their next deploy, or the note that their build path couldn't be traced. ++ ++## General tips ++- The reference files for Vite are authoritative — if this page and a reference disagree on an API, follow the reference. ++- Two different keys, two different jobs: a **personal API key** uploads maps at build time; the **public project key** powers the SDK at runtime. Don't swap them. ++- Keep build artifacts and uploaded maps in sync — every deploy should inject + upload within the same build so stack traces always resolve. ++- Uploaded maps live in PostHog and never need to be served publicly. ++- Detect the project's package manager before installing any dependency. ++- Read a file (and note its exact contents) immediately before editing it — essential for any temporary test code you'll revert afterwards. ++ ++## Framework guidelines ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md +new file mode 100644 +index 0000000..64d9113 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/COMMANDMENTS.md +@@ -0,0 +1,8 @@ ++# Framework rules ++ ++Follow these when integrating PostHog into this framework. ++ ++- A missing PostHog configuration must never break the app — read keys optionally (never a required setting), guard init and capture behind their presence, and keep build and boot working with no PostHog environment set — but never silently: in development or debug builds fail loudly, using the language's idiomatic error, with the message " variable required by PostHog is missing or un-configured, this causes events to be silently missed. This error stops appearing once is configured" (substituting the actual variable name); production stays a no-op ++- Remember that source code is available in the node_modules directory ++- Check package.json for type checking or build scripts to validate changes ++- When identity comes from framework-bridged state (Inertia or SSR shared props, a serialized session), confirm the backend actually shares that field — add the share server-side if missing — before identifying from it +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md +new file mode 100644 +index 0000000..51bfb01 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/cli.md +@@ -0,0 +1,150 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps with CLI - Docs ++ ++Copy page ++ ++# Upload source maps with CLI - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Download CLI ++ ++ Required ++ ++ Install `posthog-cli`: ++ ++ PostHog AI ++ ++ ### Npm ++ ++ ```bash ++ npm install -g @posthog/cli ++ ``` ++ ++ ### Curl ++ ++ ```bash ++ curl --proto '=https' --tlsv1.2 -LsSf https://download.posthog.com/cli | sh ++ posthog-cli-update ++ ``` ++ ++2. 2 ++ ++ ## Authenticate ++ ++ Required ++ ++ To authenticate the CLI, call the `login` command. This opens your browser where you select your organization, project, and API scopes to grant: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli login ++ ``` ++ ++ If you are using the CLI in a CI/CD environment such as GitHub Actions, you can set environment variables to authenticate: ++ ++ | Environment Variable | Description | Source | ++ | --- | --- | --- | ++ | POSTHOG_CLI_HOST | The PostHog host to connect to [default: https://us.posthog.com] | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_PROJECT_ID | PostHog project ID | [Project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_CLI_API_KEY | Personal API key with error tracking write and organization read scopes | [API key settings](https://app.posthog.com/settings/user-api-keys#variables) | ++ ++ You can also use the `--host` option instead of the `POSTHOG_CLI_HOST` environment variable to target a different PostHog instance or region. For EU users: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --host https://eu.posthog.com [CMD] ++ ``` ++ ++ If you already keep your project's configuration in a dotenv-style file, you can load these variables from it with the `--dotenv-file` option instead of exporting them: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ posthog-cli --dotenv-file .env sourcemap upload --directory ./path/to/assets ++ ``` ++ ++3. 3 ++ ++ ## Inject ++ ++ Required ++ ++ Once you've built your application and have bundled assets, inject the context required by PostHog to associate the maps with the served code. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Inject release and chunk metadata into sourcemaps ++ posthog-cli sourcemap inject --directory ./path/to/assets ++ ``` ++ ++ You can verify that the metadata has been injected by checking for the `//# chunkId=...` comment in the minified code. ++ ++4. 4 ++ ++ ## Upload ++ ++ Required ++ ++ You will then need to upload the modified assets to PostHog. ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```bash ++ # Upload injected sourcemaps to their release ++ posthog-cli sourcemap upload --directory ./path/to/assets --release-name my-app --release-version 1.2.3 --build 42 ++ ``` ++ ++ The CLI will create or reuse the [release](/docs/error-tracking/releases.md) for the detected or supplied release name and version. The CLI will try to detect release name and version information, but you can set them explicitly with `--release-name` and `--release-version`. We recommend setting the release name, and letting the CLI detect the version, if your project is continuously deployed (the version will be the git commit hash at build time). ++ ++ You can also pass `--build` to record a build number (e.g. `CFBundleVersion` on iOS, `versionCode` on Android) as release metadata. This is optional — when omitted, no build info is recorded. ++ ++ > **💡 Tip:** You can use `--delete-after` option to clean up sourcemaps after uploading them. ++ ++5. 5 ++ ++ ## Serve injected assets ++ ++ Required ++ ++ You *must* serve the injected assets in deployed production app. The injected metadata is used during error capture to identify the correct source map to use. ++ ++ If you serve a copy of the bundled assets as they were prior to running `posthog-cli sourcemap inject`, we won't be able to use the uploaded sourcemap to unminify or demangle your stack traces. ++ ++7. ## Verify source maps upload ++ ++ Checkpoint ++ ++ Confirm that source maps are successfully uploaded to PostHog.[Check symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md +new file mode 100644 +index 0000000..b6ae318 +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/upload-source-maps.md +@@ -0,0 +1,67 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps - Docs ++ ++Copy page ++ ++# Upload source maps - Docs ++ ++If you serve compiled or minified code, PostHog requires source maps to generate accurate stack traces. ++ ++If your source maps are not publicly hosted, you will need to upload them during your build process to see unminified code in your stack traces. ++ ++## AI wizard ++ ++If you're using a JavaScript or TypeScript framework, set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++Otherwise, choose your platform below for manual instructions. ++ ++## Platforms ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/js.svg)Web](/docs/error-tracking/upload-source-maps/web.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nextjs.svg)Next.js](/docs/error-tracking/upload-source-maps/nextjs.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/nodejs.svg)Node.js](/docs/error-tracking/upload-source-maps/node.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React](/docs/error-tracking/upload-source-maps/react.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/docs/integrate/frameworks/angular.svg)Angular](/docs/error-tracking/upload-source-maps/angular.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nuxt.svg)Nuxt](/docs/error-tracking/upload-source-maps/nuxt.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/react.svg)React Native](/docs/error-tracking/upload-source-maps/react-native.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Android_robot_bec2fb7318.svg)Android](/docs/error-tracking/upload-mappings/android.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/flutter.svg)Flutter](/docs/error-tracking/upload-source-maps/flutter.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/go.svg)Go](/docs/error-tracking/upload-source-maps/go.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/ios.svg)iOS](/docs/error-tracking/upload-source-maps/ios.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/kmp.svg)Kotlin Multiplatform](/docs/error-tracking/upload-debug-symbols/kmp.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/rust.svg)Rust](/docs/error-tracking/upload-source-maps/rust.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Rollup_js_c306a2fde3.svg)Rollup](/docs/error-tracking/upload-source-maps/rollup.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/webpack_3fc774b5a5.svg)Webpack](/docs/error-tracking/upload-source-maps/webpack.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/Vitejs_logo_98ffe5d5ee.svg)Vite](/docs/error-tracking/upload-source-maps/vite.md) ++ ++- [CLI](/docs/error-tracking/upload-source-maps/cli.md) ++ ++- [![](https://res.cloudinary.com/dmukukwp6/image/upload/github_mark_903e35d471.svg)GitHub Action](/docs/error-tracking/upload-source-maps/github-actions.md) ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md b/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md +new file mode 100644 +index 0000000..9465e3f +--- /dev/null ++++ b/.claude/skills/error-tracking-upload-source-maps-vite/references/vite.md +@@ -0,0 +1,103 @@ ++> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt ++ ++# Upload source maps for Vite - Docs ++ ++Copy page ++ ++# Upload source maps for Vite - Docs ++ ++## AI wizard ++ ++Set up source map uploading automatically with our wizard by running this command in your project directory with your terminal (it also works for [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt): ++ ++`npx @posthog/wizard upload-source-maps` ++ ++[Learn more](/wizard.md) ++ ++## Manual setup ++ ++1. 1 ++ ++ ## Install the PostHog Rollup plugin ++ ++ Required ++ ++ Vite uses Rollup under the hood, so you can use the PostHog Rollup plugin to upload source maps: ++ ++ Terminal ++ ++ PostHog AI ++ ++ ```shell ++ npm install @posthog/rollup-plugin ++ ``` ++ ++2. 2 ++ ++ ## Add PostHog plugin to your Vite config ++ ++ Required ++ ++ Add the PostHog plugin to your `vite.config.js` file: ++ ++ vite.config.js ++ ++ PostHog AI ++ ++ ```javascript ++ import { defineConfig } from 'vite' ++ import posthog from '@posthog/rollup-plugin' ++ export default defineConfig({ ++ plugins: [ ++ posthog({ ++ personalApiKey: process.env.POSTHOG_API_KEY!, // Personal API Key ++ projectId: process.env.POSTHOG_PROJECT_ID, // Project ID ++ host: process.env.POSTHOG_HOST, // (optional) defaults to https://us.i.posthog.com ++ sourcemaps: { // (optional) ++ enabled: true, // (optional) Enable sourcemaps generation and upload, defaults to true ++ releaseName: 'my-application', // (optional) Release name ++ releaseVersion: '1.0.0', // (optional) Release version ++ deleteAfterUpload: true, // (optional) Delete sourcemaps after upload, defaults to true ++ }, ++ }), ++ ], ++ }) ++ ``` ++ ++ Set the following environment variables: ++ ++ | Environment variable | Description | ++ | --- | --- | ++ | POSTHOG_API_KEY | [Personal API key](https://app.posthog.com/settings/user-api-keys#variables) with at least write access on error tracking | ++ | POSTHOG_PROJECT_ID | Project ID you can find in your [project settings](https://app.posthog.com/settings/project#variables) | ++ | POSTHOG_HOST | (optional) Your PostHog instance URL. Defaults to https://us.i.posthog.com | ++ ++ **Using CI/CD?** ++ ++ Add these environment variables to your CI/CD service's project settings to automatically upload source maps during production builds. ++ ++3. ## Verify source map upload and injection ++ ++ Checkpoint ++ ++ Confirm source maps were successfully uploaded: ++ ++ 1. Go to your [symbol sets in PostHog](https://app.posthog.com/error_tracking/configuration#selectedSetting=error-tracking-symbol-sets) and verify your latest upload appears. ++ ++ 2. Check your production JavaScript files in browser dev tools. They should include a source map reference comment: ++ ++ JavaScript ++ ++ PostHog AI ++ ++ ```javascript ++ //# chunkId=0197e6db-9a73-7b91-9e80-4e1b7158db5c ++ ``` ++ ++### Still have questions? ++ ++Ask PostHog AI ++ ++### Was this page useful? ++ ++HelpfulCould be better +\ No newline at end of file +diff --git a/.gitignore b/.gitignore +index a547bf3..438657a 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -11,6 +11,7 @@ node_modules + dist + dist-ssr + *.local ++.env + + # Editor directories and files + .vscode/* +diff --git a/package-lock.json b/package-lock.json +index 782aa1a..cdacb31 100644 +--- a/package-lock.json ++++ b/package-lock.json +@@ -13,6 +13,7 @@ + "react-dom": "^19.2.6" + }, + "devDependencies": { ++ "@posthog/rollup-plugin": "^1.5.1", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", +@@ -54,6 +55,31 @@ + "tslib": "^2.4.0" + } + }, ++ "node_modules/@jridgewell/sourcemap-codec": { ++ "version": "1.5.5", ++ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", ++ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", ++ "dev": true, ++ "license": "MIT" ++ }, ++ "node_modules/@napi-rs/lzma-linux-x64-gnu": { ++ "version": "1.5.1", ++ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", ++ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true, ++ "engines": { ++ "node": "^22.20 || ^24.12 || >=25" ++ } ++ }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", +@@ -329,6 +355,51 @@ + "url": "https://github.com/sponsors/Boshen" + } + }, ++ "node_modules/@posthog/cli": { ++ "version": "0.14.1", ++ "resolved": "https://registry.npmjs.org/@posthog/cli/-/cli-0.14.1.tgz", ++ "integrity": "sha512-gTzcKpl9TZLf0LrlLHEjChlPc9LIK1gdQG0alMnX6+b+W1mTD+6nTN0W/MeEzjT4DiKDeK8FPhc1n7dT1tWKFw==", ++ "dev": true, ++ "hasInstallScript": true, ++ "hasShrinkwrap": true, ++ "license": "MIT", ++ "dependencies": { ++ "detect-libc": "^2.1.2" ++ }, ++ "bin": { ++ "posthog-cli": "run-posthog-cli.js" ++ }, ++ "engines": { ++ "node": ">=14.14", ++ "npm": ">=6" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/detect-libc": { ++ "version": "2.1.2", ++ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", ++ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", ++ "dev": true, ++ "license": "Apache-2.0", ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/@posthog/cli/node_modules/prettier": { ++ "version": "3.8.3", ++ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", ++ "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", ++ "extraneous": true, ++ "license": "MIT", ++ "bin": { ++ "prettier": "bin/prettier.cjs" ++ }, ++ "engines": { ++ "node": ">=14" ++ }, ++ "funding": { ++ "url": "https://github.com/prettier/prettier?sponsor=1" ++ } ++ }, + "node_modules/@posthog/core": { + "version": "1.29.11", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.29.11.tgz", +@@ -338,6 +409,31 @@ + "@posthog/types": "1.376.2" + } + }, ++ "node_modules/@posthog/plugin-utils": { ++ "version": "1.2.0", ++ "resolved": "https://registry.npmjs.org/@posthog/plugin-utils/-/plugin-utils-1.2.0.tgz", ++ "integrity": "sha512-SXG2oVxPnliYKmixyIYqPv1CA4UYPZy9fQL5H+mvN/OQpKioRTawp8I2ofQmf6SfiYpnf+KAzLiRUlK7S3rOCw==", ++ "dev": true, ++ "license": "MIT", ++ "dependencies": { ++ "cross-spawn": "^7.0.6" ++ } ++ }, ++ "node_modules/@posthog/rollup-plugin": { ++ "version": "1.5.1", ++ "resolved": "https://registry.npmjs.org/@posthog/rollup-plugin/-/rollup-plugin-1.5.1.tgz", ++ "integrity": "sha512-4ZNhhgnMEdRXP5YlLngHEwaaobrpNhHMT7JY4kZ1uLjGeURRbq7OjWNNg2MGN6iJgR8iIbvbt4Kho1WxflCz6A==", ++ "dev": true, ++ "license": "MIT", ++ "dependencies": { ++ "@posthog/cli": "~0.14.1", ++ "@posthog/plugin-utils": "^1.2.0", ++ "magic-string": "^0.30.17" ++ }, ++ "peerDependencies": { ++ "rollup": ">= 4.0.0" ++ } ++ }, + "node_modules/@posthog/types": { + "version": "1.376.2", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.376.2.tgz", +@@ -500,9 +596,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -520,9 +613,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -540,9 +630,6 @@ + "ppc64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -560,9 +647,6 @@ + "s390x" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -580,9 +664,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -600,9 +681,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MIT", + "optional": true, + "os": [ +@@ -689,6 +767,381 @@ + "dev": true, + "license": "MIT" + }, ++ "node_modules/@rollup/rollup-android-arm-eabi": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.0.tgz", ++ "integrity": "sha512-70TeIFezKKy65LgAVyQh+w94/gjWhvPWaLaGGeMEgVrPkQhuj/M5bAYYZzIFUj9Y69oHyTm5Um/R6gcLh4A8JA==", ++ "cpu": [ ++ "arm" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "android" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-android-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.0.tgz", ++ "integrity": "sha512-YC86tYIHK6M1IV+wbzO+Bxk8RCBr6ZyWYgWxUCzaZD8mc8rrFoIJDNzDrkHBYRc/wKdrsIXmm6/F7NzrAO+OrA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "android" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-darwin-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.0.tgz", ++ "integrity": "sha512-oI+ECtUcli0y0fi4xpW82GdPIXdTkI8G8DSjG2LRuw09fPAGykaWYH/hXxiKuTxiAjiPSTIIuYUqof5Z2hShWw==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "darwin" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-darwin-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.0.tgz", ++ "integrity": "sha512-NwV+1s7TiKrMe4owHyKB/dTLD7ZJD0YEBEhIz+hvav1Cu1GReJjF+rsdNwjzENQeIAbE/CoNiaAc5Vz2h5DPAA==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "darwin" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-freebsd-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.0.tgz", ++ "integrity": "sha512-tWtHBTu5gOPK4u4Urtk4qAHW3zZ9rQAmbssO8gp7ELvGTGI3aCiq6NqyTQ0PCIg7KbHJF2UkGDDs77YZGxfjCA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "freebsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-freebsd-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.0.tgz", ++ "integrity": "sha512-2qPoJiwTvtHQ27NnYvTnsgk8laXWYuVmNESG8WFZBcEPKLfZ3I27qBJarjVRQtwGeYyRfq5ZowHXih9lm2BItw==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "freebsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm-gnueabihf": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.0.tgz", ++ "integrity": "sha512-FQwsTRvLNuHoTdICABJQfbPUSEueISGmnpT06tXTMpfprf5NiKLSXKA0A+w45wJnCmZAnzgqBwbt6ARFuyOi5w==", ++ "cpu": [ ++ "arm" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm-musleabihf": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.0.tgz", ++ "integrity": "sha512-BBVTXziw8mY1a4ZbWME9tZyfzqXCDPqaC7Z3heQ29p5dkvXzwL0NwelO8zLa8c3RBKvl3YTuSnBgsBhYBtwjIw==", ++ "cpu": [ ++ "arm" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.0.tgz", ++ "integrity": "sha512-w2Iyy9+RqKwx3d9qWMKsJg0FfRBsY0/pXNv0mCQ3ueRvJI6+QAScfD4nrMlzFLs2HNVW6Ew+mtZfDl9b7Ew5/Q==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-arm64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.0.tgz", ++ "integrity": "sha512-YK++KtrFRHYE0P6/RtYEAy9t8F37znP+K03RrIuLPYOL6SVlObRumf/0OE4V/h63xL9DwkWbNssZfmA9hawuDA==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-loong64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.0.tgz", ++ "integrity": "sha512-aBfOG6fP7YkkPmTqPwufRJeFyz7WPpECv9XNbnsk9+vg7rxdih0lbtEel7jcRng4LZrrmU3FfitCFyEj4BWDWg==", ++ "cpu": [ ++ "loong64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-loong64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.0.tgz", ++ "integrity": "sha512-LGaHEOeHNAag9VuS1Crs5DFg4RrU9MPi2nVnNJk9DTePx/B6RRYKVmrIXt2h7YOJlwjaFJ6lwtFDliZxScTLrQ==", ++ "cpu": [ ++ "loong64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-ppc64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.0.tgz", ++ "integrity": "sha512-jClvk+J0FC3b7Udvegiw5/4hErbHtmsNsQgENnKXDWtNCJXsJYZH5WURvu7imDOO38xYml24eeh5x3A04ppwCw==", ++ "cpu": [ ++ "ppc64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-ppc64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.0.tgz", ++ "integrity": "sha512-0OJlaGK+8+B777Ql5okIpD7ua5Ro9+VB9Ve0OKa28OQJZ1RbuUBVNHK/e3pr4BROqsyPl1JrPO1ZxJseCNffcA==", ++ "cpu": [ ++ "ppc64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-riscv64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.0.tgz", ++ "integrity": "sha512-Ygsx+HoNH7afwi1bTIXbnTvVnsO+zurPLSYxybV1hHFVU72OWOCl6v05ql/z0hkpAPx+DK7Kn9Bi7MayCcjLTA==", ++ "cpu": [ ++ "riscv64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-riscv64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.0.tgz", ++ "integrity": "sha512-pDQxtMGb+OvG3fLwR2OkZlSd47hW+kWg4BYMG/++sR6RqorQccwPTDsxda5hPwiIeIErAnCF9ma3SAU06bdQtQ==", ++ "cpu": [ ++ "riscv64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-s390x-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.0.tgz", ++ "integrity": "sha512-0BnUG9mS8I4SSHr3XsxVhuCMEiu+rX61xxZF5vujso4LaiAGFZFxvDjg6Xn6tLPNTUAfuCvQYas4LMQMVsKRSQ==", ++ "cpu": [ ++ "s390x" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-x64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.0.tgz", ++ "integrity": "sha512-Adu/VttB1dpPNW+FEacrZ+xVm9tFty84+RrFzsqlFaPxoJB+9XXyDGtp5dCOoBwGBIEVH0To7lExFXEx0BIF4A==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-linux-x64-musl": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.0.tgz", ++ "integrity": "sha512-NQ3bDvjUbFKmP23671xUlXtKmqVsUBd6M4PQCvbmNtOy06hnQIdKHy8oG/6S3R/S6He1JgPk6A5VT+prAJMYEw==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "linux" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-openbsd-x64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.0.tgz", ++ "integrity": "sha512-u2eDAl4+0aFvA13GxlGBtTI3SS3sdgwgtV0HyjZ0QaQVCgNE+jqNGey+GtxWiq+wxr/UycAx/OnfJzApCFamvA==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "openbsd" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-openharmony-arm64": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.0.tgz", ++ "integrity": "sha512-XvRb5vfW3wAZQ+ZUG21AnHHDKtNcw99eigzEhjr//NZ3u7SoBaPP0seSc7FgP7p1epAEdAoZckMW9WY/+4w70w==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "openharmony" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-arm64-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.0.tgz", ++ "integrity": "sha512-iZPmniy4kNBf5yo2RezbkYNNK5HPbXE9+g+twnbqSng7dtLEJy1SKoxiE/ni4FDacjyuZpEeb9U054N4EoKHYw==", ++ "cpu": [ ++ "arm64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-ia32-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.0.tgz", ++ "integrity": "sha512-mFBBd+LF37fnE8JnYUOH+imj0aPFPK30vpar4ehJkgnLj9sZn8ZxiRENmLtgIwxK7TC8klF6N57fxdNBwQoqOA==", ++ "cpu": [ ++ "ia32" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-x64-gnu": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.0.tgz", ++ "integrity": "sha512-ujeqEY3B+zbGn3Z4Q03cUBG/LGWnBJncVT36WER31LcOsQk9+1dmINKKtvmmfChUvRbK1G0R8OhMWFgHgaZtAw==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, ++ "node_modules/@rollup/rollup-win32-x64-msvc": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.0.tgz", ++ "integrity": "sha512-hncn90N4sOky0L2LKE5oESKLbxCPeVo4eLA2LSMoDzM+879ml4WSr+Rr4DWknNIVVvS1Hirkc9hx02W6YxS8rQ==", ++ "cpu": [ ++ "x64" ++ ], ++ "dev": true, ++ "license": "MIT", ++ "optional": true, ++ "os": [ ++ "win32" ++ ], ++ "peer": true ++ }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", +@@ -700,6 +1153,14 @@ + "tslib": "^2.4.0" + } + }, ++ "node_modules/@types/estree": { ++ "version": "1.0.9", ++ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", ++ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", ++ "dev": true, ++ "license": "MIT", ++ "peer": true ++ }, + "node_modules/@types/node": { + "version": "25.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", +@@ -773,6 +1234,21 @@ + "url": "https://opencollective.com/core-js" + } + }, ++ "node_modules/cross-spawn": { ++ "version": "7.0.6", ++ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", ++ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", ++ "dev": true, ++ "license": "MIT", ++ "dependencies": { ++ "path-key": "^3.1.0", ++ "shebang-command": "^2.0.0", ++ "which": "^2.0.1" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } ++ }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", +@@ -838,6 +1314,13 @@ + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, ++ "node_modules/isexe": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", ++ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", ++ "dev": true, ++ "license": "ISC" ++ }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", +@@ -981,9 +1464,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1005,9 +1485,6 @@ + "arm64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1029,9 +1506,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "glibc" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1053,9 +1527,6 @@ + "x64" + ], + "dev": true, +- "libc": [ +- "musl" +- ], + "license": "MPL-2.0", + "optional": true, + "os": [ +@@ -1117,6 +1588,16 @@ + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, ++ "node_modules/magic-string": { ++ "version": "0.30.21", ++ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", ++ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", ++ "dev": true, ++ "license": "MIT", ++ "dependencies": { ++ "@jridgewell/sourcemap-codec": "^1.5.5" ++ } ++ }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", +@@ -1136,6 +1617,16 @@ + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, ++ "node_modules/path-key": { ++ "version": "3.1.1", ++ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", ++ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", ++ "dev": true, ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", +@@ -1301,12 +1792,82 @@ + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, ++ "node_modules/rollup": { ++ "version": "4.63.0", ++ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.0.tgz", ++ "integrity": "sha512-T5vnZ2y4QqC3/4P+w2+JO+Q/OVdnPsv4XcSYJYMEn0R9/jjl5AgLwO9LAZMzP2lN71O6pypn91rB7lDstUkfrQ==", ++ "dev": true, ++ "license": "MIT", ++ "peer": true, ++ "dependencies": { ++ "@types/estree": "1.0.9" ++ }, ++ "bin": { ++ "rollup": "dist/bin/rollup" ++ }, ++ "engines": { ++ "node": ">=18.0.0", ++ "npm": ">=8.0.0" ++ }, ++ "optionalDependencies": { ++ "@napi-rs/lzma-linux-x64-gnu": "1.5.1", ++ "@rollup/rollup-android-arm-eabi": "4.63.0", ++ "@rollup/rollup-android-arm64": "4.63.0", ++ "@rollup/rollup-darwin-arm64": "4.63.0", ++ "@rollup/rollup-darwin-x64": "4.63.0", ++ "@rollup/rollup-freebsd-arm64": "4.63.0", ++ "@rollup/rollup-freebsd-x64": "4.63.0", ++ "@rollup/rollup-linux-arm-gnueabihf": "4.63.0", ++ "@rollup/rollup-linux-arm-musleabihf": "4.63.0", ++ "@rollup/rollup-linux-arm64-gnu": "4.63.0", ++ "@rollup/rollup-linux-arm64-musl": "4.63.0", ++ "@rollup/rollup-linux-loong64-gnu": "4.63.0", ++ "@rollup/rollup-linux-loong64-musl": "4.63.0", ++ "@rollup/rollup-linux-ppc64-gnu": "4.63.0", ++ "@rollup/rollup-linux-ppc64-musl": "4.63.0", ++ "@rollup/rollup-linux-riscv64-gnu": "4.63.0", ++ "@rollup/rollup-linux-riscv64-musl": "4.63.0", ++ "@rollup/rollup-linux-s390x-gnu": "4.63.0", ++ "@rollup/rollup-linux-x64-gnu": "4.63.0", ++ "@rollup/rollup-linux-x64-musl": "4.63.0", ++ "@rollup/rollup-openbsd-x64": "4.63.0", ++ "@rollup/rollup-openharmony-arm64": "4.63.0", ++ "@rollup/rollup-win32-arm64-msvc": "4.63.0", ++ "@rollup/rollup-win32-ia32-msvc": "4.63.0", ++ "@rollup/rollup-win32-x64-gnu": "4.63.0", ++ "@rollup/rollup-win32-x64-msvc": "4.63.0", ++ "fsevents": "~2.3.2" ++ } ++ }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, ++ "node_modules/shebang-command": { ++ "version": "2.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", ++ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", ++ "dev": true, ++ "license": "MIT", ++ "dependencies": { ++ "shebang-regex": "^3.0.0" ++ }, ++ "engines": { ++ "node": ">=8" ++ } ++ }, ++ "node_modules/shebang-regex": { ++ "version": "3.0.0", ++ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", ++ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", ++ "dev": true, ++ "license": "MIT", ++ "engines": { ++ "node": ">=8" ++ } ++ }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", +@@ -1445,6 +2006,22 @@ + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.2.0.tgz", + "integrity": "sha512-i2z98bEmaCqSDiHEDu+gHl/dmR4Q+TxFmG3/13KkMO+o8UxQzCqWaDRCiLgEa41nlO4VpXSI0ASa1xWmO9sBlA==", + "license": "Apache-2.0" ++ }, ++ "node_modules/which": { ++ "version": "2.0.2", ++ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", ++ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", ++ "dev": true, ++ "license": "ISC", ++ "dependencies": { ++ "isexe": "^2.0.0" ++ }, ++ "bin": { ++ "node-which": "bin/node-which" ++ }, ++ "engines": { ++ "node": ">= 8" ++ } + } + } + } +diff --git a/package.json b/package.json +index 6042ca5..446d003 100644 +--- a/package.json ++++ b/package.json +@@ -14,6 +14,7 @@ + "react-dom": "^19.2.6" + }, + "devDependencies": { ++ "@posthog/rollup-plugin": "^1.5.1", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", +diff --git a/vite.config.ts b/vite.config.ts +index 8b0f57b..27269ef 100644 +--- a/vite.config.ts ++++ b/vite.config.ts +@@ -1,7 +1,23 @@ +-import { defineConfig } from 'vite' ++import posthog from '@posthog/rollup-plugin' + import react from '@vitejs/plugin-react' ++import { defineConfig, loadEnv } from 'vite' + + // https://vite.dev/config/ +-export default defineConfig({ +- plugins: [react()], ++export default defineConfig(({ mode }) => { ++ process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') } ++ ++ return { ++ plugins: [ ++ react(), ++ posthog({ ++ personalApiKey: process.env.POSTHOG_API_KEY!, ++ projectId: process.env.POSTHOG_PROJECT_ID, ++ host: process.env.POSTHOG_HOST, ++ sourcemaps: { ++ enabled: true, ++ deleteAfterUpload: true, ++ }, ++ }), ++ ], ++ } + }) diff --git a/results/source-maps-sol-medium/react-vite__sol-medium/result.json b/results/source-maps-sol-medium/react-vite__sol-medium/result.json new file mode 100644 index 000000000..9e7e6dd67 --- /dev/null +++ b/results/source-maps-sol-medium/react-vite__sol-medium/result.json @@ -0,0 +1,24 @@ +{ + "runPhase": "completed", + "hasPosthogDep": true, + "newDeps": [ + "posthog-js", + "@posthog/rollup-plugin" + ], + "envFile": "/tmp/sm-run-react-vite__sol-medium/.env", + "screenPath": [ + "source-maps-intro", + "auth", + "source-maps-detect", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "wizard-ask", + "run", + "source-maps-outro", + "keep-skills" + ], + "skillsComplete": true +} \ No newline at end of file